{
  "openapi": "3.1.1",
  "info": {
    "title": "Paduraksa admin API",
    "description": "The REST API of the Paduraksa admin app. Authenticate with an API token (Authorization: Bearer ...) or a signed-in session. Paths are kebab-case; JSON members are camelCase; IDs are typed strings (host_...); times are ISO 8601 in UTC; enums are strings. A create is POST to the collection: 201 with Location and the created object. A change is PUT or PATCH: 200 with the object. A delete is 204. Every collection takes limit and cursor and answers { items, nextCursor }. Every error is RFC 9457 problem details (application/problem+json) with a traceId. Config objects (hosts, routes, upstreams, profiles, the settings, ...) and rollbacks take optimistic concurrency: a read carries an ETag (an object's version, or the latest revision's ID), and a change sends it back as one quoted tag in If-Match. Without it the answer is 428; a stale or weak tag is 412 (the problem's conflict says what is current); an unquoted tag, a list or '*' is 400. Creating with POST needs none, and neither do the objects that carry no version (certificates, ACME accounts, DNS providers, notification channels, users). Changes to config objects also take ?dryRun=true, which returns the plan and commits nothing. A change that was stored but could not go live is 500 with code config.publishFailed, the stored revision and the new ETag. Each operation names the permission it needs (x-required-permission).",
    "version": "v1"
  },
  "servers": [
    {
      "url": "/",
      "description": "The admin app that serves this document."
    }
  ],
  "paths": {
    "/api/v1/setup": {
      "get": {
        "tags": [
          "Identity"
        ],
        "summary": "Whether first-run setup is done",
        "description": "Anonymous: it needs no credentials.",
        "operationId": "GetSetupStatus",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SetupStatus"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [ ]
      },
      "post": {
        "tags": [
          "Identity"
        ],
        "summary": "Complete first-run setup",
        "description": "Creates the Owner with the setup token the server prints and signs them in (the answer's session). Works once: 409 setup.complete after that.\n\nAnonymous: it needs no credentials.",
        "operationId": "CompleteSetup",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SetupRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SetupCompleted"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller may not do this.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "409": {
            "description": "It clashes with what exists, or with something in progress.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "415": {
            "description": "The body is not in a type this operation takes.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "422": {
            "description": "The request is well-formed but can't be applied; errors lists why, with a pointer into the body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "429": {
            "description": "Too many attempts; Retry-After says when to try again.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "503": {
            "description": "The server can't do this right now; try again shortly.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [ ]
      }
    },
    "/api/v1/auth/login": {
      "post": {
        "tags": [
          "Identity"
        ],
        "summary": "Sign in with a username and password",
        "description": "Sets the session cookie. A user with a second factor gets a session that is not yet complete (secondFactorRequired): finish it with auth/second-factor. A refusal is always the same generic 401.\n\nAnonymous: it needs no credentials.",
        "operationId": "Login",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LoginRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SessionStarted"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "415": {
            "description": "The body is not in a type this operation takes.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "429": {
            "description": "Too many attempts; Retry-After says when to try again.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "503": {
            "description": "The server can't do this right now; try again shortly.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [ ]
      }
    },
    "/api/v1/auth/second-factor": {
      "post": {
        "tags": [
          "Identity"
        ],
        "summary": "Finish signing in with a code",
        "description": "Send either the authenticator's code or one recovery code.\n\nAnonymous: it needs no credentials.",
        "operationId": "CompleteSecondFactor",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SecondFactorRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SessionStarted"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "415": {
            "description": "The body is not in a type this operation takes.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "429": {
            "description": "Too many attempts; Retry-After says when to try again.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "503": {
            "description": "The server can't do this right now; try again shortly.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [ ]
      }
    },
    "/api/v1/auth/logout": {
      "post": {
        "tags": [
          "Identity"
        ],
        "summary": "Sign out",
        "description": "Ends the session of the cookie and clears it; 204 whether or not there was one.\n\nAnonymous: it needs no credentials.",
        "operationId": "Logout",
        "responses": {
          "204": {
            "description": "No Content"
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [ ]
      }
    },
    "/api/v1/auth/me": {
      "get": {
        "tags": [
          "Identity"
        ],
        "summary": "Who the caller is and what they may do",
        "description": "Requires a signed-in caller; what the caller may do is checked by the operation itself.",
        "operationId": "GetMe",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CurrentCaller"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ]
      }
    },
    "/api/v1/me/password": {
      "post": {
        "tags": [
          "Identity"
        ],
        "summary": "Change your password",
        "description": "Needs the current password (a wrong one is 422 and counts toward the lockout) and ends every other session of the caller.\n\nRequires a signed-in caller; what the caller may do is checked by the operation itself.",
        "operationId": "ChangePassword",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ChangePasswordRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "204": {
            "description": "No Content"
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller may not do this.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "415": {
            "description": "The body is not in a type this operation takes.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "422": {
            "description": "The request is well-formed but can't be applied; errors lists why, with a pointer into the body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "503": {
            "description": "The server can't do this right now; try again shortly.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ]
      }
    },
    "/api/v1/me/totp/enrollment": {
      "post": {
        "tags": [
          "Identity"
        ],
        "summary": "Start enrolling an authenticator",
        "description": "Needs the password. The provisioning URI carries the secret and is shown once; the enrolment is not active until it is confirmed.\n\nRequires a signed-in caller; what the caller may do is checked by the operation itself.",
        "operationId": "BeginTotpEnrollment",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PasswordConfirmation"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TotpEnrollmentStarted"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller may not do this.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "409": {
            "description": "It clashes with what exists, or with something in progress.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "415": {
            "description": "The body is not in a type this operation takes.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "422": {
            "description": "The request is well-formed but can't be applied; errors lists why, with a pointer into the body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "503": {
            "description": "The server can't do this right now; try again shortly.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ]
      }
    },
    "/api/v1/me/totp/enrollment/confirm": {
      "post": {
        "tags": [
          "Identity"
        ],
        "summary": "Confirm the authenticator with a code",
        "description": "Activates the second factor and returns the recovery codes, shown once.\n\nRequires a signed-in caller; what the caller may do is checked by the operation itself.",
        "operationId": "ConfirmTotpEnrollment",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TotpConfirmation"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RecoveryCodes"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller may not do this.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "409": {
            "description": "It clashes with what exists, or with something in progress.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "415": {
            "description": "The body is not in a type this operation takes.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "422": {
            "description": "The request is well-formed but can't be applied; errors lists why, with a pointer into the body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ]
      }
    },
    "/api/v1/me/totp/disable": {
      "post": {
        "tags": [
          "Identity"
        ],
        "summary": "Turn the second factor off",
        "description": "Needs the password, so it is a POST with a body (a DELETE has no body to rely on).\n\nRequires a signed-in caller; what the caller may do is checked by the operation itself.",
        "operationId": "DisableTotp",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PasswordConfirmation"
              }
            }
          },
          "required": true
        },
        "responses": {
          "204": {
            "description": "No Content"
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller may not do this.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "409": {
            "description": "It clashes with what exists, or with something in progress.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "415": {
            "description": "The body is not in a type this operation takes.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "422": {
            "description": "The request is well-formed but can't be applied; errors lists why, with a pointer into the body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "503": {
            "description": "The server can't do this right now; try again shortly.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ]
      }
    },
    "/api/v1/me/recovery-codes": {
      "post": {
        "tags": [
          "Identity"
        ],
        "summary": "Make new recovery codes",
        "description": "Needs the password. The old codes stop working; the new ones are shown once.\n\nRequires a signed-in caller; what the caller may do is checked by the operation itself.",
        "operationId": "RegenerateRecoveryCodes",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PasswordConfirmation"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RecoveryCodes"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller may not do this.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "409": {
            "description": "It clashes with what exists, or with something in progress.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "415": {
            "description": "The body is not in a type this operation takes.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "422": {
            "description": "The request is well-formed but can't be applied; errors lists why, with a pointer into the body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "503": {
            "description": "The server can't do this right now; try again shortly.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ]
      }
    },
    "/api/v1/sessions": {
      "get": {
        "tags": [
          "Identity"
        ],
        "summary": "List sessions",
        "description": "A signed-in session is required: a token caller gets 403 auth.sessionRequired.\n\nRequires a signed-in caller; what the caller may do is checked by the operation itself.",
        "operationId": "ListSessions",
        "parameters": [
          {
            "name": "userId",
            "in": "query",
            "description": "List this user's instead of the caller's (the service checks the caller may); 404 when it names no user.",
            "schema": {
              "examples": [
                "usr_0A1b2C3d4E5f6G7h8I9j0K"
              ],
              "pattern": "^usr_[0-9A-Za-z]{22}$",
              "type": "string",
              "description": "An admin user; its entity is `UserRow` in Data."
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "How many items a page holds, 1 to 200 (default 50).",
            "schema": {
              "maximum": 200,
              "minimum": 1,
              "type": "integer",
              "default": 50
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "The nextCursor of the previous page, to continue after it. An opaque string only the API that issued it understands.",
            "schema": {
              "maxLength": 96,
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PageOfSessionSummary"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller may not do this.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ]
      }
    },
    "/api/v1/sessions/revoke-others": {
      "post": {
        "tags": [
          "Identity"
        ],
        "summary": "End every other session of the caller",
        "description": "Requires a signed-in caller; what the caller may do is checked by the operation itself.",
        "operationId": "RevokeOtherSessions",
        "responses": {
          "204": {
            "description": "No Content"
          },
          "403": {
            "description": "The caller may not do this.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ]
      }
    },
    "/api/v1/sessions/{id}": {
      "delete": {
        "tags": [
          "Identity"
        ],
        "summary": "End a session",
        "description": "Requires a signed-in caller; what the caller may do is checked by the operation itself.",
        "operationId": "RevokeSession",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "examples": [
                "ses_0A1b2C3d4E5f6G7h8I9j0K"
              ],
              "pattern": "^ses_[0-9A-Za-z]{22}$",
              "type": "string",
              "description": "An admin session. UuidV4: a session's ID is shown in the session list and handed to the dashboard, so it\nshould not tell when the session began. It is a handle, not a credential: the cookie value is the credential, a\nseparate 256-bit random value of which only the hash is stored."
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "403": {
            "description": "The caller may not do this.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ]
      }
    },
    "/api/v1/tokens": {
      "get": {
        "tags": [
          "Identity"
        ],
        "summary": "List API tokens",
        "description": "A signed-in session is required: a token caller gets 403 auth.sessionRequired.\n\nRequires a signed-in caller; what the caller may do is checked by the operation itself.",
        "operationId": "ListTokens",
        "parameters": [
          {
            "name": "userId",
            "in": "query",
            "description": "List this user's instead of the caller's (the service checks the caller may); 404 when it names no user.",
            "schema": {
              "examples": [
                "usr_0A1b2C3d4E5f6G7h8I9j0K"
              ],
              "pattern": "^usr_[0-9A-Za-z]{22}$",
              "type": "string",
              "description": "An admin user; its entity is `UserRow` in Data."
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "How many items a page holds, 1 to 200 (default 50).",
            "schema": {
              "maximum": 200,
              "minimum": 1,
              "type": "integer",
              "default": 50
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "The nextCursor of the previous page, to continue after it. An opaque string only the API that issued it understands.",
            "schema": {
              "maxLength": 96,
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PageOfApiTokenSummary"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller may not do this.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ]
      },
      "post": {
        "tags": [
          "Identity"
        ],
        "summary": "Issue an API token",
        "description": "The token's secret is in the answer and nowhere else, ever again.\n\nRequires a signed-in caller; what the caller may do is checked by the operation itself.",
        "operationId": "IssueToken",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/IssueApiTokenRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IssuedApiToken"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller may not do this.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "415": {
            "description": "The body is not in a type this operation takes.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "422": {
            "description": "The request is well-formed but can't be applied; errors lists why, with a pointer into the body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ]
      }
    },
    "/api/v1/tokens/{id}": {
      "delete": {
        "tags": [
          "Identity"
        ],
        "summary": "Revoke an API token",
        "description": "Requires a signed-in caller; what the caller may do is checked by the operation itself.",
        "operationId": "RevokeToken",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "examples": [
                "tok_0A1b2C3d4E5f6G7h8I9j0K"
              ],
              "pattern": "^tok_[0-9A-Za-z]{22}$",
              "type": "string",
              "description": "An API token."
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "403": {
            "description": "The caller may not do this.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ]
      }
    },
    "/api/v1/users": {
      "get": {
        "tags": [
          "Identity"
        ],
        "summary": "List users",
        "description": "Requires the `users:read` permission.",
        "operationId": "ListUsers",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "description": "How many items a page holds, 1 to 200 (default 50).",
            "schema": {
              "maximum": 200,
              "minimum": 1,
              "type": "integer",
              "default": 50
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "The nextCursor of the previous page, to continue after it. An opaque string only the API that issued it understands.",
            "schema": {
              "maxLength": 96,
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PageOfUserSummary"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller may not do this.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "users:read"
      },
      "post": {
        "tags": [
          "Identity"
        ],
        "summary": "Create a user",
        "description": "Requires the `users:write` permission.",
        "operationId": "CreateUser",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateUserRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserSummary"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller may not do this.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "409": {
            "description": "It clashes with what exists, or with something in progress.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "415": {
            "description": "The body is not in a type this operation takes.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "422": {
            "description": "The request is well-formed but can't be applied; errors lists why, with a pointer into the body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "users:write"
      }
    },
    "/api/v1/users/{id}": {
      "get": {
        "tags": [
          "Identity"
        ],
        "summary": "Get a user",
        "description": "A user may read themselves; reading another needs users:read.\n\nRequires a signed-in caller; what the caller may do is checked by the operation itself.",
        "operationId": "GetUser",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "examples": [
                "usr_0A1b2C3d4E5f6G7h8I9j0K"
              ],
              "pattern": "^usr_[0-9A-Za-z]{22}$",
              "type": "string",
              "description": "An admin user; its entity is `UserRow` in Data."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserSummary"
                }
              }
            }
          },
          "403": {
            "description": "The caller may not do this.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ]
      },
      "patch": {
        "tags": [
          "Identity"
        ],
        "summary": "Change a user",
        "description": "A partial update: the display name, roles and disabled flag that are given. Users carry no version, so there is no If-Match: the last write wins.\n\nRequires the `users:write` permission.",
        "operationId": "UpdateUser",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "examples": [
                "usr_0A1b2C3d4E5f6G7h8I9j0K"
              ],
              "pattern": "^usr_[0-9A-Za-z]{22}$",
              "type": "string",
              "description": "An admin user; its entity is `UserRow` in Data."
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateUserRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserSummary"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller may not do this.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "409": {
            "description": "It clashes with what exists, or with something in progress.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "415": {
            "description": "The body is not in a type this operation takes.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "422": {
            "description": "The request is well-formed but can't be applied; errors lists why, with a pointer into the body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "users:write"
      },
      "delete": {
        "tags": [
          "Identity"
        ],
        "summary": "Delete a user",
        "description": "Requires the `users:write` permission.",
        "operationId": "DeleteUser",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "examples": [
                "usr_0A1b2C3d4E5f6G7h8I9j0K"
              ],
              "pattern": "^usr_[0-9A-Za-z]{22}$",
              "type": "string",
              "description": "An admin user; its entity is `UserRow` in Data."
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "403": {
            "description": "The caller may not do this.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "409": {
            "description": "It clashes with what exists, or with something in progress.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "users:write"
      }
    },
    "/api/v1/version": {
      "get": {
        "tags": [
          "Server"
        ],
        "summary": "The version and build commit",
        "description": "The product version and the commit it was built from. The API is versioned by its path (v1); a change that breaks it would be a v2.\n\nRequires a signed-in caller; what the caller may do is checked by the operation itself.",
        "operationId": "GetVersion",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VersionInfo"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ]
      }
    },
    "/api/v1/config": {
      "get": {
        "tags": [
          "Config"
        ],
        "summary": "The current config document",
        "description": "The whole config as the latest revision has it, versions included. The ETag is the latest revision's ID. It includes access lists' users with their PBKDF2 password verifiers (never a password); see the access list endpoints.\n\nRequires the `config:read` permission.",
        "operationId": "GetConfig",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CurrentConfig"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `config:read` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "config:read"
      }
    },
    "/api/v1/settings": {
      "get": {
        "tags": [
          "Settings"
        ],
        "summary": "The global settings",
        "description": "The ETag is the settings' version.\n\nRequires the `config:read` permission.",
        "operationId": "GetSettings",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GlobalSettings"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `config:read` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "config:read"
      },
      "put": {
        "tags": [
          "Settings"
        ],
        "summary": "Replace the global settings",
        "description": "Needs If-Match with the settings' version (428 without it, 412 when stale).\n\nRequires the `config:write` permission.",
        "operationId": "PutSettings",
        "parameters": [
          {
            "name": "dryRun",
            "in": "query",
            "description": "true validates and compiles the change and returns the plan without committing it.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "force",
            "in": "query",
            "description": "true overrides the admin host's lock-out guard, for a change that would remove the caller's own way in.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "message",
            "in": "query",
            "description": "A note stored with the revision this change makes (at most 1000 characters).",
            "schema": {
              "maxLength": 1000,
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GlobalSettings"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConfigCommandResult"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "412": {
            "description": "The If-Match tag is stale (or weak): what you read is no longer current. The problem says what is.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "415": {
            "description": "The body is not in a type this operation takes.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "422": {
            "description": "The request is well-formed but can't be applied; errors lists why, with a pointer into the body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "428": {
            "description": "The change needs an If-Match header with the ETag of what you read.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `config:write` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "Either an unexpected error (the trace ID finds it in the server log), or code config.publishFailed: the change was stored (revision, and the new version in version and the ETag header) but could not go live; the previous config still serves.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "config:write"
      }
    },
    "/api/v1/server-settings": {
      "get": {
        "tags": [
          "Settings"
        ],
        "summary": "The server settings",
        "description": "Every server setting with its default, its bounds, what the config stores, the value in force and where it comes from: the server's environment (an explicit Paduraksa__... variable or appsettings key, which wins), the config, or the default. A setting that applies after a restart also names the value the process runs with. The ETag is the global settings' version.\n\nRequires the `config:read` permission.",
        "operationId": "GetServerSettings",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServerSettingsView"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `config:read` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "config:read"
      },
      "put": {
        "tags": [
          "Settings"
        ],
        "summary": "Replace the server settings",
        "description": "The body is what the config stores: a member left out or null goes back to its default. Values are held to each setting's bounds (422, server.setting.invalid). A value the environment sets is stored but not used while the environment sets it. Needs server:write besides config:write, and If-Match with the global settings' version (428 without it, 412 when stale).\n\nRequires the `server:write` permission.",
        "operationId": "PutServerSettings",
        "parameters": [
          {
            "name": "dryRun",
            "in": "query",
            "description": "true validates and compiles the change and returns the plan without committing it.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "force",
            "in": "query",
            "description": "true overrides the admin host's lock-out guard, for a change that would remove the caller's own way in.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "message",
            "in": "query",
            "description": "A note stored with the revision this change makes (at most 1000 characters).",
            "schema": {
              "maxLength": 1000,
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ServerSettings"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConfigCommandResult"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "412": {
            "description": "The If-Match tag is stale (or weak): what you read is no longer current. The problem says what is.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "415": {
            "description": "The body is not in a type this operation takes.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "422": {
            "description": "The request is well-formed but can't be applied; errors lists why, with a pointer into the body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "428": {
            "description": "The change needs an If-Match header with the ETag of what you read.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `server:write` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "Either an unexpected error (the trace ID finds it in the server log), or code config.publishFailed: the change was stored (revision, and the new version in version and the ETag header) but could not go live; the previous config still serves.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "server:write"
      }
    },
    "/api/v1/config/export": {
      "get": {
        "tags": [
          "Config"
        ],
        "summary": "The config as YAML",
        "description": "The latest revision's document, or the one asked for, in the YAML format of docs/design/admin-surfaces.md, YAML import and export. The ETag is the revision exported. It holds no secret: an access list's users are a username and a credentialId, never a password. Importing it in replace mode on a fresh instance gives the same revision hash.\n\nRequires the `config:read` permission.",
        "operationId": "ExportConfig",
        "parameters": [
          {
            "name": "revision",
            "in": "query",
            "description": "Export this revision (rev_...) instead of the latest.",
            "schema": {
              "examples": [
                "rev_0A1b2C3d4E5f6G7h8I9j0K"
              ],
              "pattern": "^rev_[0-9A-Za-z]{22}$",
              "type": "string",
              "description": "A committed config revision."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/yaml": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `config:read` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "config:read"
      }
    },
    "/api/v1/config/import": {
      "post": {
        "tags": [
          "Config"
        ],
        "summary": "Import a YAML config",
        "description": "The body is YAML (application/yaml), at most 4 MiB. The mode says whether what the YAML leaves out is kept or deleted. Objects are matched by id, else by name (a host by its first host name). A dry run runs every check and returns the changes and warnings without committing. If-Match, when sent, is the revision the YAML was prepared against (412 when it is no longer the latest). A user's credentialId must be one the vault holds a password under (422 otherwise, in a dry run too). Errors (422) point into the YAML, with its line.\n\nRequires the `config:write` permission.",
        "operationId": "ImportConfig",
        "parameters": [
          {
            "name": "mode",
            "in": "query",
            "description": "merge adds or updates the objects the YAML names and deletes nothing; replace makes the config the YAML, deleting what it leaves out.",
            "schema": {
              "enum": [
                "merge",
                "replace"
              ],
              "type": "string",
              "default": "merge"
            }
          },
          {
            "name": "dryRun",
            "in": "query",
            "description": "true validates and compiles the change and returns the plan without committing it.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "force",
            "in": "query",
            "description": "true overrides the admin host's lock-out guard, for a change that would remove the caller's own way in.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "message",
            "in": "query",
            "description": "A note stored with the revision this change makes (at most 1000 characters).",
            "schema": {
              "maxLength": 1000,
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/yaml": {
              "schema": {
                "type": "string"
              }
            },
            "application/x-yaml": {
              "schema": {
                "type": "string"
              }
            },
            "text/yaml": {
              "schema": {
                "type": "string"
              }
            },
            "text/x-yaml": {
              "schema": {
                "type": "string"
              }
            },
            "text/plain": {
              "schema": {
                "type": "string"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConfigCommandResult"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller may not do this.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "409": {
            "description": "It clashes with what exists, or with something in progress.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "412": {
            "description": "The If-Match tag is stale (or weak): what you read is no longer current. The problem says what is.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "413": {
            "description": "The body is larger than this operation takes.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "415": {
            "description": "The body is not in a type this operation takes.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "422": {
            "description": "The request is well-formed but can't be applied; errors lists why, with a pointer into the body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "Either an unexpected error (the trace ID finds it in the server log), or code config.publishFailed: the change was stored (revision, and the new version in version and the ETag header) but could not go live; the previous config still serves.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "config:write"
      }
    },
    "/api/v1/acme-orders": {
      "get": {
        "tags": [
          "Certificates"
        ],
        "summary": "The ACME order state of every ACME certificate",
        "description": "Requires the `certificates:read` permission.",
        "operationId": "ListAcmeOrders",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "description": "How many items a page holds, 1 to 200 (default 50).",
            "schema": {
              "maximum": 200,
              "minimum": 1,
              "type": "integer",
              "default": 50
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "The nextCursor of the previous page, to continue after it. An opaque string only the API that issued it understands.",
            "schema": {
              "maxLength": 96,
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PageOfAcmeOrderSummary"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `certificates:read` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "certificates:read"
      }
    },
    "/api/v1/backup": {
      "post": {
        "tags": [
          "Backup"
        ],
        "summary": "Download a full backup",
        "description": "Streams one archive (application/octet-stream, .pdkbak) holding the config database, the vault database and the last-known-good config, all encrypted under the passphrase in the body (at least 12 characters) with AES-256-GCM and an Argon2id key. The vault's keys in it are wrapped under a key made for this archive, so it restores on another machine with that machine's own master key, and it is useless without the passphrase, which nothing can recover. The server keeps running and the databases are read consistently. Making one takes about a second of CPU and 64 MiB; one at a time (a second request while one runs gets 409). The passphrase goes in the JSON body over the admin's HTTPS, never in a URL.\n\nRequires the `backup:create` permission.",
        "operationId": "CreateBackup",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BackupRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/octet-stream": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "409": {
            "description": "It clashes with what exists, or with something in progress.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "415": {
            "description": "The body is not in a type this operation takes.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "422": {
            "description": "The request is well-formed but can't be applied; errors lists why, with a pointer into the body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `backup:create` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "backup:create"
      }
    },
    "/api/v1/error-pages/preview": {
      "post": {
        "tags": [
          "Error pages"
        ],
        "summary": "Render an error page with sample values",
        "description": "The page is judged by the rules that saving it is (error paths point into the request: /page/heading, /page/text, /page/html, /status), then rendered by the proxy's own writer with a sample request ID, host and CF-Ray; html is the document it would serve, to be shown in a sandboxed frame without scripts, and contentSecurityPolicy the policy it is served with. Nothing is stored.\n\nRequires the `config:read` permission.",
        "operationId": "PreviewErrorPage",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ErrorPagePreviewRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorPagePreview"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "415": {
            "description": "The body is not in a type this operation takes.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "422": {
            "description": "The request is well-formed but can't be applied; errors lists why, with a pointer into the body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `config:read` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "config:read"
      }
    },
    "/api/v1/simulate": {
      "post": {
        "tags": [
          "Simulator"
        ],
        "summary": "Simulate a request against the live config, a revision or a draft",
        "description": "target is {kind: live} (the default), {kind: revision, revision}, {kind: draftDocument, document}, {kind: draftCommand, command} or {kind: draft, draft: <draft id>} (your own stored draft, applied as its review does; it needs config:write like reading the draft, 404 when it is not yours); a draft is validated and compiled as a real change would be, and never stored or published. Nothing here changes any counter or state.\n\nRequires the `simulate` permission.",
        "operationId": "Simulate",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SimulateBody"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SimulationResult"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "409": {
            "description": "It clashes with what exists, or with something in progress.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "410": {
            "description": "Gone",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "415": {
            "description": "The body is not in a type this operation takes.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "422": {
            "description": "The request is well-formed but can't be applied; errors lists why, with a pointer into the body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "503": {
            "description": "The server can't do this right now; try again shortly.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `simulate` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "simulate"
      }
    },
    "/api/v1/hosts": {
      "get": {
        "tags": [
          "Hosts"
        ],
        "summary": "List hosts",
        "description": "Requires the `config:read` permission.",
        "operationId": "ListHosts",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "description": "How many items a page holds, 1 to 200 (default 50).",
            "schema": {
              "maximum": 200,
              "minimum": 1,
              "type": "integer",
              "default": 50
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "The nextCursor of the previous page, to continue after it. An opaque string only the API that issued it understands.",
            "schema": {
              "maxLength": 96,
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PageOfHostConfig"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `config:read` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "config:read"
      },
      "post": {
        "tags": [
          "Hosts"
        ],
        "summary": "Create a host",
        "description": "The body is the host with a client-chosen ID; its version is ignored (a new object is version 0). 409 when the ID exists.\n\nRequires the `config:write` permission.",
        "operationId": "CreateHost",
        "parameters": [
          {
            "name": "dryRun",
            "in": "query",
            "description": "true validates and compiles the change and returns the plan without committing it.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "force",
            "in": "query",
            "description": "true overrides the admin host's lock-out guard, for a change that would remove the caller's own way in.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "message",
            "in": "query",
            "description": "A note stored with the revision this change makes (at most 1000 characters).",
            "schema": {
              "maxLength": 1000,
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/HostConfig"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConfigCommandResult"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "409": {
            "description": "It clashes with what exists, or with something in progress.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "415": {
            "description": "The body is not in a type this operation takes.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "422": {
            "description": "The request is well-formed but can't be applied; errors lists why, with a pointer into the body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `config:write` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "Either an unexpected error (the trace ID finds it in the server log), or code config.publishFailed: the change was stored (revision, and the new version in version and the ETag header) but could not go live; the previous config still serves.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "config:write"
      }
    },
    "/api/v1/hosts/{id}": {
      "get": {
        "tags": [
          "Hosts"
        ],
        "summary": "Get a host",
        "description": "Requires the `config:read` permission.",
        "operationId": "GetHost",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "examples": [
                "host_0A1b2C3d4E5f6G7h8I9j0K"
              ],
              "pattern": "^host_[0-9A-Za-z]{22}$",
              "type": "string",
              "description": "A host."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HostConfig"
                }
              }
            }
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `config:read` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "config:read"
      },
      "put": {
        "tags": [
          "Hosts"
        ],
        "summary": "Replace a host",
        "description": "Replaces the host and its routes. Needs If-Match with the version you read (428 without it, 412 when stale). The body's ID must be the path's.\n\nRequires the `config:write` permission.",
        "operationId": "ReplaceHost",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "examples": [
                "host_0A1b2C3d4E5f6G7h8I9j0K"
              ],
              "pattern": "^host_[0-9A-Za-z]{22}$",
              "type": "string",
              "description": "A host."
            }
          },
          {
            "name": "dryRun",
            "in": "query",
            "description": "true validates and compiles the change and returns the plan without committing it.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "force",
            "in": "query",
            "description": "true overrides the admin host's lock-out guard, for a change that would remove the caller's own way in.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "message",
            "in": "query",
            "description": "A note stored with the revision this change makes (at most 1000 characters).",
            "schema": {
              "maxLength": 1000,
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/HostConfig"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConfigCommandResult"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "412": {
            "description": "The If-Match tag is stale (or weak): what you read is no longer current. The problem says what is.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "415": {
            "description": "The body is not in a type this operation takes.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "422": {
            "description": "The request is well-formed but can't be applied; errors lists why, with a pointer into the body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "428": {
            "description": "The change needs an If-Match header with the ETag of what you read.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `config:write` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "Either an unexpected error (the trace ID finds it in the server log), or code config.publishFailed: the change was stored (revision, and the new version in version and the ETag header) but could not go live; the previous config still serves.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "config:write"
      },
      "delete": {
        "tags": [
          "Hosts"
        ],
        "summary": "Delete a host and its routes",
        "description": "Requires the `config:write` permission.",
        "operationId": "DeleteHost",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "examples": [
                "host_0A1b2C3d4E5f6G7h8I9j0K"
              ],
              "pattern": "^host_[0-9A-Za-z]{22}$",
              "type": "string",
              "description": "A host."
            }
          },
          {
            "name": "dryRun",
            "in": "query",
            "description": "true validates and compiles the change and returns the plan without committing it.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "force",
            "in": "query",
            "description": "true overrides the admin host's lock-out guard, for a change that would remove the caller's own way in.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "message",
            "in": "query",
            "description": "A note stored with the revision this change makes (at most 1000 characters).",
            "schema": {
              "maxLength": 1000,
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConfigCommandResult"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "412": {
            "description": "The If-Match tag is stale (or weak): what you read is no longer current. The problem says what is.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "428": {
            "description": "The change needs an If-Match header with the ETag of what you read.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `config:write` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "Either an unexpected error (the trace ID finds it in the server log), or code config.publishFailed: the change was stored (revision, and the new version in version and the ETag header) but could not go live; the previous config still serves.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "config:write"
      }
    },
    "/api/v1/hosts/{hostId}/routes": {
      "get": {
        "tags": [
          "Hosts"
        ],
        "summary": "List a host's routes",
        "description": "Requires the `config:read` permission.",
        "operationId": "ListRoutes",
        "parameters": [
          {
            "name": "hostId",
            "in": "path",
            "required": true,
            "schema": {
              "examples": [
                "host_0A1b2C3d4E5f6G7h8I9j0K"
              ],
              "pattern": "^host_[0-9A-Za-z]{22}$",
              "type": "string",
              "description": "A host."
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "How many items a page holds, 1 to 200 (default 50).",
            "schema": {
              "maximum": 200,
              "minimum": 1,
              "type": "integer",
              "default": 50
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "The nextCursor of the previous page, to continue after it. An opaque string only the API that issued it understands.",
            "schema": {
              "maxLength": 96,
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PageOfRouteConfig"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `config:read` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "config:read"
      }
    },
    "/api/v1/hosts/{hostId}/routes/{routeId}": {
      "get": {
        "tags": [
          "Hosts"
        ],
        "summary": "Get a route",
        "description": "Requires the `config:read` permission.",
        "operationId": "GetRoute",
        "parameters": [
          {
            "name": "hostId",
            "in": "path",
            "required": true,
            "schema": {
              "examples": [
                "host_0A1b2C3d4E5f6G7h8I9j0K"
              ],
              "pattern": "^host_[0-9A-Za-z]{22}$",
              "type": "string",
              "description": "A host."
            }
          },
          {
            "name": "routeId",
            "in": "path",
            "required": true,
            "schema": {
              "examples": [
                "rte_0A1b2C3d4E5f6G7h8I9j0K"
              ],
              "pattern": "^rte_[0-9A-Za-z]{22}$",
              "type": "string",
              "description": "A route of a proxy host."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouteConfig"
                }
              }
            }
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `config:read` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "config:read"
      },
      "put": {
        "tags": [
          "Hosts"
        ],
        "summary": "Create or replace a route of a host",
        "description": "A route belongs to its host: this is a change of the host, so If-Match is the host's version. A new route goes last (201 with Location); error paths are relative to the host.\n\nRequires the `config:write` permission.",
        "operationId": "PutRoute",
        "parameters": [
          {
            "name": "hostId",
            "in": "path",
            "required": true,
            "schema": {
              "examples": [
                "host_0A1b2C3d4E5f6G7h8I9j0K"
              ],
              "pattern": "^host_[0-9A-Za-z]{22}$",
              "type": "string",
              "description": "A host."
            }
          },
          {
            "name": "routeId",
            "in": "path",
            "required": true,
            "schema": {
              "examples": [
                "rte_0A1b2C3d4E5f6G7h8I9j0K"
              ],
              "pattern": "^rte_[0-9A-Za-z]{22}$",
              "type": "string",
              "description": "A route of a proxy host."
            }
          },
          {
            "name": "dryRun",
            "in": "query",
            "description": "true validates and compiles the change and returns the plan without committing it.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "force",
            "in": "query",
            "description": "true overrides the admin host's lock-out guard, for a change that would remove the caller's own way in.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "message",
            "in": "query",
            "description": "A note stored with the revision this change makes (at most 1000 characters).",
            "schema": {
              "maxLength": 1000,
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RouteConfig"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConfigCommandResult"
                }
              }
            }
          },
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConfigCommandResult"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "412": {
            "description": "The If-Match tag is stale (or weak): what you read is no longer current. The problem says what is.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "415": {
            "description": "The body is not in a type this operation takes.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "422": {
            "description": "The request is well-formed but can't be applied; errors lists why, with a pointer into the body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "428": {
            "description": "The change needs an If-Match header with the ETag of what you read.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `config:write` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "Either an unexpected error (the trace ID finds it in the server log), or code config.publishFailed: the change was stored (revision, and the new version in version and the ETag header) but could not go live; the previous config still serves.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "config:write"
      },
      "delete": {
        "tags": [
          "Hosts"
        ],
        "summary": "Delete a route of a host",
        "description": "Requires the `config:write` permission.",
        "operationId": "DeleteRoute",
        "parameters": [
          {
            "name": "hostId",
            "in": "path",
            "required": true,
            "schema": {
              "examples": [
                "host_0A1b2C3d4E5f6G7h8I9j0K"
              ],
              "pattern": "^host_[0-9A-Za-z]{22}$",
              "type": "string",
              "description": "A host."
            }
          },
          {
            "name": "routeId",
            "in": "path",
            "required": true,
            "schema": {
              "examples": [
                "rte_0A1b2C3d4E5f6G7h8I9j0K"
              ],
              "pattern": "^rte_[0-9A-Za-z]{22}$",
              "type": "string",
              "description": "A route of a proxy host."
            }
          },
          {
            "name": "dryRun",
            "in": "query",
            "description": "true validates and compiles the change and returns the plan without committing it.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "force",
            "in": "query",
            "description": "true overrides the admin host's lock-out guard, for a change that would remove the caller's own way in.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "message",
            "in": "query",
            "description": "A note stored with the revision this change makes (at most 1000 characters).",
            "schema": {
              "maxLength": 1000,
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConfigCommandResult"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "412": {
            "description": "The If-Match tag is stale (or weak): what you read is no longer current. The problem says what is.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "428": {
            "description": "The change needs an If-Match header with the ETag of what you read.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `config:write` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "Either an unexpected error (the trace ID finds it in the server log), or code config.publishFailed: the change was stored (revision, and the new version in version and the ETag header) but could not go live; the previous config still serves.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "config:write"
      }
    },
    "/api/v1/upstreams": {
      "get": {
        "tags": [
          "Upstreams"
        ],
        "summary": "List upstreams",
        "description": "Requires the `config:read` permission.",
        "operationId": "ListUpstreams",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "description": "How many items a page holds, 1 to 200 (default 50).",
            "schema": {
              "maximum": 200,
              "minimum": 1,
              "type": "integer",
              "default": 50
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "The nextCursor of the previous page, to continue after it. An opaque string only the API that issued it understands.",
            "schema": {
              "maxLength": 96,
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PageOfUpstreamConfig"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `config:read` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "config:read"
      },
      "post": {
        "tags": [
          "Upstreams"
        ],
        "summary": "Create an upstream",
        "description": "Requires the `config:write` permission.",
        "operationId": "CreateUpstream",
        "parameters": [
          {
            "name": "dryRun",
            "in": "query",
            "description": "true validates and compiles the change and returns the plan without committing it.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "force",
            "in": "query",
            "description": "true overrides the admin host's lock-out guard, for a change that would remove the caller's own way in.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "message",
            "in": "query",
            "description": "A note stored with the revision this change makes (at most 1000 characters).",
            "schema": {
              "maxLength": 1000,
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpstreamConfig"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConfigCommandResult"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "409": {
            "description": "It clashes with what exists, or with something in progress.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "415": {
            "description": "The body is not in a type this operation takes.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "422": {
            "description": "The request is well-formed but can't be applied; errors lists why, with a pointer into the body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `config:write` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "Either an unexpected error (the trace ID finds it in the server log), or code config.publishFailed: the change was stored (revision, and the new version in version and the ETag header) but could not go live; the previous config still serves.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "config:write"
      }
    },
    "/api/v1/upstreams/{id}": {
      "get": {
        "tags": [
          "Upstreams"
        ],
        "summary": "Get an upstream",
        "description": "Requires the `config:read` permission.",
        "operationId": "GetUpstream",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "examples": [
                "ups_0A1b2C3d4E5f6G7h8I9j0K"
              ],
              "pattern": "^ups_[0-9A-Za-z]{22}$",
              "type": "string",
              "description": "An upstream, compiled into a YARP cluster."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UpstreamConfig"
                }
              }
            }
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `config:read` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "config:read"
      },
      "put": {
        "tags": [
          "Upstreams"
        ],
        "summary": "Replace an upstream",
        "description": "Requires the `config:write` permission.",
        "operationId": "ReplaceUpstream",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "examples": [
                "ups_0A1b2C3d4E5f6G7h8I9j0K"
              ],
              "pattern": "^ups_[0-9A-Za-z]{22}$",
              "type": "string",
              "description": "An upstream, compiled into a YARP cluster."
            }
          },
          {
            "name": "dryRun",
            "in": "query",
            "description": "true validates and compiles the change and returns the plan without committing it.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "force",
            "in": "query",
            "description": "true overrides the admin host's lock-out guard, for a change that would remove the caller's own way in.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "message",
            "in": "query",
            "description": "A note stored with the revision this change makes (at most 1000 characters).",
            "schema": {
              "maxLength": 1000,
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpstreamConfig"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConfigCommandResult"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "412": {
            "description": "The If-Match tag is stale (or weak): what you read is no longer current. The problem says what is.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "415": {
            "description": "The body is not in a type this operation takes.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "422": {
            "description": "The request is well-formed but can't be applied; errors lists why, with a pointer into the body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "428": {
            "description": "The change needs an If-Match header with the ETag of what you read.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `config:write` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "Either an unexpected error (the trace ID finds it in the server log), or code config.publishFailed: the change was stored (revision, and the new version in version and the ETag header) but could not go live; the previous config still serves.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "config:write"
      },
      "delete": {
        "tags": [
          "Upstreams"
        ],
        "summary": "Delete an upstream",
        "description": "Refused (422, relatedErrors) while a route still uses it.\n\nRequires the `config:write` permission.",
        "operationId": "DeleteUpstream",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "examples": [
                "ups_0A1b2C3d4E5f6G7h8I9j0K"
              ],
              "pattern": "^ups_[0-9A-Za-z]{22}$",
              "type": "string",
              "description": "An upstream, compiled into a YARP cluster."
            }
          },
          {
            "name": "dryRun",
            "in": "query",
            "description": "true validates and compiles the change and returns the plan without committing it.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "force",
            "in": "query",
            "description": "true overrides the admin host's lock-out guard, for a change that would remove the caller's own way in.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "message",
            "in": "query",
            "description": "A note stored with the revision this change makes (at most 1000 characters).",
            "schema": {
              "maxLength": 1000,
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConfigCommandResult"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "412": {
            "description": "The If-Match tag is stale (or weak): what you read is no longer current. The problem says what is.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "422": {
            "description": "The request is well-formed but can't be applied; errors lists why, with a pointer into the body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "428": {
            "description": "The change needs an If-Match header with the ETag of what you read.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `config:write` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "Either an unexpected error (the trace ID finds it in the server log), or code config.publishFailed: the change was stored (revision, and the new version in version and the ETag header) but could not go live; the previous config still serves.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "config:write"
      }
    },
    "/api/v1/rate-limit-policies": {
      "get": {
        "tags": [
          "Rate limits"
        ],
        "summary": "List rate-limit policies",
        "description": "Requires the `config:read` permission.",
        "operationId": "ListRateLimitPolicies",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "description": "How many items a page holds, 1 to 200 (default 50).",
            "schema": {
              "maximum": 200,
              "minimum": 1,
              "type": "integer",
              "default": 50
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "The nextCursor of the previous page, to continue after it. An opaque string only the API that issued it understands.",
            "schema": {
              "maxLength": 96,
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PageOfRateLimitPolicyConfig"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `config:read` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "config:read"
      },
      "post": {
        "tags": [
          "Rate limits"
        ],
        "summary": "Create a rate-limit policy",
        "description": "Requires the `config:write` permission.",
        "operationId": "CreateRateLimitPolicy",
        "parameters": [
          {
            "name": "dryRun",
            "in": "query",
            "description": "true validates and compiles the change and returns the plan without committing it.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "force",
            "in": "query",
            "description": "true overrides the admin host's lock-out guard, for a change that would remove the caller's own way in.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "message",
            "in": "query",
            "description": "A note stored with the revision this change makes (at most 1000 characters).",
            "schema": {
              "maxLength": 1000,
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RateLimitPolicyConfig"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConfigCommandResult"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "409": {
            "description": "It clashes with what exists, or with something in progress.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "415": {
            "description": "The body is not in a type this operation takes.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "422": {
            "description": "The request is well-formed but can't be applied; errors lists why, with a pointer into the body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `config:write` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "Either an unexpected error (the trace ID finds it in the server log), or code config.publishFailed: the change was stored (revision, and the new version in version and the ETag header) but could not go live; the previous config still serves.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "config:write"
      }
    },
    "/api/v1/rate-limit-policies/{id}": {
      "get": {
        "tags": [
          "Rate limits"
        ],
        "summary": "Get a rate-limit policy",
        "description": "Requires the `config:read` permission.",
        "operationId": "GetRateLimitPolicy",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "examples": [
                "rlp_0A1b2C3d4E5f6G7h8I9j0K"
              ],
              "pattern": "^rlp_[0-9A-Za-z]{22}$",
              "type": "string",
              "description": "A rate-limit policy: one token bucket's rate, burst and partition key, which hosts and routes name."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateLimitPolicyConfig"
                }
              }
            }
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `config:read` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "config:read"
      },
      "put": {
        "tags": [
          "Rate limits"
        ],
        "summary": "Replace a rate-limit policy",
        "description": "Requires the `config:write` permission.",
        "operationId": "ReplaceRateLimitPolicy",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "examples": [
                "rlp_0A1b2C3d4E5f6G7h8I9j0K"
              ],
              "pattern": "^rlp_[0-9A-Za-z]{22}$",
              "type": "string",
              "description": "A rate-limit policy: one token bucket's rate, burst and partition key, which hosts and routes name."
            }
          },
          {
            "name": "dryRun",
            "in": "query",
            "description": "true validates and compiles the change and returns the plan without committing it.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "force",
            "in": "query",
            "description": "true overrides the admin host's lock-out guard, for a change that would remove the caller's own way in.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "message",
            "in": "query",
            "description": "A note stored with the revision this change makes (at most 1000 characters).",
            "schema": {
              "maxLength": 1000,
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RateLimitPolicyConfig"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConfigCommandResult"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "412": {
            "description": "The If-Match tag is stale (or weak): what you read is no longer current. The problem says what is.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "415": {
            "description": "The body is not in a type this operation takes.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "422": {
            "description": "The request is well-formed but can't be applied; errors lists why, with a pointer into the body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "428": {
            "description": "The change needs an If-Match header with the ETag of what you read.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `config:write` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "Either an unexpected error (the trace ID finds it in the server log), or code config.publishFailed: the change was stored (revision, and the new version in version and the ETag header) but could not go live; the previous config still serves.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "config:write"
      },
      "delete": {
        "tags": [
          "Rate limits"
        ],
        "summary": "Delete a rate-limit policy",
        "description": "Refused (422, relatedErrors) while a host or route still names it.\n\nRequires the `config:write` permission.",
        "operationId": "DeleteRateLimitPolicy",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "examples": [
                "rlp_0A1b2C3d4E5f6G7h8I9j0K"
              ],
              "pattern": "^rlp_[0-9A-Za-z]{22}$",
              "type": "string",
              "description": "A rate-limit policy: one token bucket's rate, burst and partition key, which hosts and routes name."
            }
          },
          {
            "name": "dryRun",
            "in": "query",
            "description": "true validates and compiles the change and returns the plan without committing it.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "force",
            "in": "query",
            "description": "true overrides the admin host's lock-out guard, for a change that would remove the caller's own way in.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "message",
            "in": "query",
            "description": "A note stored with the revision this change makes (at most 1000 characters).",
            "schema": {
              "maxLength": 1000,
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConfigCommandResult"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "412": {
            "description": "The If-Match tag is stale (or weak): what you read is no longer current. The problem says what is.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "422": {
            "description": "The request is well-formed but can't be applied; errors lists why, with a pointer into the body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "428": {
            "description": "The change needs an If-Match header with the ETag of what you read.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `config:write` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "Either an unexpected error (the trace ID finds it in the server log), or code config.publishFailed: the change was stored (revision, and the new version in version and the ETag header) but could not go live; the previous config still serves.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "config:write"
      }
    },
    "/api/v1/access-lists": {
      "get": {
        "tags": [
          "Access lists"
        ],
        "summary": "List access lists",
        "description": "Users carry 'credentialId', the vault's handle on their password. No read returns a password or a hash.\n\nRequires the `config:read` permission.",
        "operationId": "ListAccessLists",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "description": "How many items a page holds, 1 to 200 (default 50).",
            "schema": {
              "maximum": 200,
              "minimum": 1,
              "type": "integer",
              "default": 50
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "The nextCursor of the previous page, to continue after it. An opaque string only the API that issued it understands.",
            "schema": {
              "maxLength": 96,
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PageOfAccessListConfig"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `config:read` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "config:read"
      },
      "post": {
        "tags": [
          "Access lists"
        ],
        "summary": "Create an access list",
        "description": "A user's plain 'password' goes to the vault before the list is validated or stored, and is never returned; the list keeps the user's 'credentialId'.\n\nRequires the `config:write` permission.",
        "operationId": "CreateAccessList",
        "parameters": [
          {
            "name": "dryRun",
            "in": "query",
            "description": "true validates and compiles the change and returns the plan without committing it.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "force",
            "in": "query",
            "description": "true overrides the admin host's lock-out guard, for a change that would remove the caller's own way in.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "message",
            "in": "query",
            "description": "A note stored with the revision this change makes (at most 1000 characters).",
            "schema": {
              "maxLength": 1000,
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AccessListConfig"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConfigCommandResult"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "409": {
            "description": "It clashes with what exists, or with something in progress.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "415": {
            "description": "The body is not in a type this operation takes.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "422": {
            "description": "The request is well-formed but can't be applied; errors lists why, with a pointer into the body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `config:write` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "Either an unexpected error (the trace ID finds it in the server log), or code config.publishFailed: the change was stored (revision, and the new version in version and the ETag header) but could not go live; the previous config still serves.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "config:write"
      }
    },
    "/api/v1/access-lists/{id}": {
      "get": {
        "tags": [
          "Access lists"
        ],
        "summary": "Get an access list",
        "description": "Requires the `config:read` permission.",
        "operationId": "GetAccessList",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "examples": [
                "acl_0A1b2C3d4E5f6G7h8I9j0K"
              ],
              "pattern": "^acl_[0-9A-Za-z]{22}$",
              "type": "string",
              "description": "An access list: IP allow and deny entries and basic-auth users, which hosts and routes name."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccessListConfig"
                }
              }
            }
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `config:read` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "config:read"
      },
      "put": {
        "tags": [
          "Access lists"
        ],
        "summary": "Replace an access list",
        "description": "A user sent without 'password' or 'credentialId' keeps the password the list has for that username. A password change commits a revision (the list is modified) that records no password.\n\nRequires the `config:write` permission.",
        "operationId": "ReplaceAccessList",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "examples": [
                "acl_0A1b2C3d4E5f6G7h8I9j0K"
              ],
              "pattern": "^acl_[0-9A-Za-z]{22}$",
              "type": "string",
              "description": "An access list: IP allow and deny entries and basic-auth users, which hosts and routes name."
            }
          },
          {
            "name": "dryRun",
            "in": "query",
            "description": "true validates and compiles the change and returns the plan without committing it.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "force",
            "in": "query",
            "description": "true overrides the admin host's lock-out guard, for a change that would remove the caller's own way in.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "message",
            "in": "query",
            "description": "A note stored with the revision this change makes (at most 1000 characters).",
            "schema": {
              "maxLength": 1000,
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AccessListConfig"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConfigCommandResult"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "412": {
            "description": "The If-Match tag is stale (or weak): what you read is no longer current. The problem says what is.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "415": {
            "description": "The body is not in a type this operation takes.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "422": {
            "description": "The request is well-formed but can't be applied; errors lists why, with a pointer into the body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "428": {
            "description": "The change needs an If-Match header with the ETag of what you read.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `config:write` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "Either an unexpected error (the trace ID finds it in the server log), or code config.publishFailed: the change was stored (revision, and the new version in version and the ETag header) but could not go live; the previous config still serves.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "config:write"
      },
      "delete": {
        "tags": [
          "Access lists"
        ],
        "summary": "Delete an access list",
        "description": "Refused (422, relatedErrors) while a host, profile, global default or route still names it.\n\nRequires the `config:write` permission.",
        "operationId": "DeleteAccessList",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "examples": [
                "acl_0A1b2C3d4E5f6G7h8I9j0K"
              ],
              "pattern": "^acl_[0-9A-Za-z]{22}$",
              "type": "string",
              "description": "An access list: IP allow and deny entries and basic-auth users, which hosts and routes name."
            }
          },
          {
            "name": "dryRun",
            "in": "query",
            "description": "true validates and compiles the change and returns the plan without committing it.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "force",
            "in": "query",
            "description": "true overrides the admin host's lock-out guard, for a change that would remove the caller's own way in.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "message",
            "in": "query",
            "description": "A note stored with the revision this change makes (at most 1000 characters).",
            "schema": {
              "maxLength": 1000,
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConfigCommandResult"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "412": {
            "description": "The If-Match tag is stale (or weak): what you read is no longer current. The problem says what is.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "422": {
            "description": "The request is well-formed but can't be applied; errors lists why, with a pointer into the body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "428": {
            "description": "The change needs an If-Match header with the ETag of what you read.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `config:write` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "Either an unexpected error (the trace ID finds it in the server log), or code config.publishFailed: the change was stored (revision, and the new version in version and the ETag header) but could not go live; the previous config still serves.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "config:write"
      }
    },
    "/api/v1/rule-sets": {
      "get": {
        "tags": [
          "Rule sets"
        ],
        "summary": "List rule sets",
        "description": "The document's own sets. The built-in presets are at /rule-sets/presets and are named in a setting like any other set.\n\nRequires the `config:read` permission.",
        "operationId": "ListRuleSets",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "description": "How many items a page holds, 1 to 200 (default 50).",
            "schema": {
              "maximum": 200,
              "minimum": 1,
              "type": "integer",
              "default": 50
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "The nextCursor of the previous page, to continue after it. An opaque string only the API that issued it understands.",
            "schema": {
              "maxLength": 96,
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PageOfRuleSetConfig"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `config:read` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "config:read"
      },
      "post": {
        "tags": [
          "Rule sets"
        ],
        "summary": "Create a rule set",
        "description": "Rules are tried in order and the first match decides. A regex that can't be built, or a rule that states no condition, is refused (422).\n\nRequires the `config:write` permission.",
        "operationId": "CreateRuleSet",
        "parameters": [
          {
            "name": "dryRun",
            "in": "query",
            "description": "true validates and compiles the change and returns the plan without committing it.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "force",
            "in": "query",
            "description": "true overrides the admin host's lock-out guard, for a change that would remove the caller's own way in.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "message",
            "in": "query",
            "description": "A note stored with the revision this change makes (at most 1000 characters).",
            "schema": {
              "maxLength": 1000,
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RuleSetConfig"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConfigCommandResult"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "409": {
            "description": "It clashes with what exists, or with something in progress.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "415": {
            "description": "The body is not in a type this operation takes.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "422": {
            "description": "The request is well-formed but can't be applied; errors lists why, with a pointer into the body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `config:write` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "Either an unexpected error (the trace ID finds it in the server log), or code config.publishFailed: the change was stored (revision, and the new version in version and the ETag header) but could not go live; the previous config still serves.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "config:write"
      }
    },
    "/api/v1/rule-sets/presets": {
      "get": {
        "tags": [
          "Rule sets"
        ],
        "summary": "List the built-in rule-set presets",
        "description": "Read-only, refreshed with each release. Copy one into a set of your own to change it.\n\nRequires the `config:read` permission.",
        "operationId": "ListRuleSetPresets",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "description": "How many items a page holds, 1 to 200 (default 50).",
            "schema": {
              "maximum": 200,
              "minimum": 1,
              "type": "integer",
              "default": 50
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "The nextCursor of the previous page, to continue after it. An opaque string only the API that issued it understands.",
            "schema": {
              "maxLength": 96,
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PageOfRuleSetConfig"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `config:read` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "config:read"
      }
    },
    "/api/v1/rule-sets/{id}": {
      "get": {
        "tags": [
          "Rule sets"
        ],
        "summary": "Get a rule set",
        "description": "Requires the `config:read` permission.",
        "operationId": "GetRuleSet",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "examples": [
                "rs_0A1b2C3d4E5f6G7h8I9j0K"
              ],
              "pattern": "^rs_[0-9A-Za-z]{22}$",
              "type": "string",
              "description": "A rule set: an ordered list of path, method and user-agent rules, which hosts and routes name. The built-in presets\n            have reserved IDs."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RuleSetConfig"
                }
              }
            }
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `config:read` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "config:read"
      },
      "put": {
        "tags": [
          "Rule sets"
        ],
        "summary": "Replace a rule set",
        "description": "Requires the `config:write` permission.",
        "operationId": "ReplaceRuleSet",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "examples": [
                "rs_0A1b2C3d4E5f6G7h8I9j0K"
              ],
              "pattern": "^rs_[0-9A-Za-z]{22}$",
              "type": "string",
              "description": "A rule set: an ordered list of path, method and user-agent rules, which hosts and routes name. The built-in presets\n            have reserved IDs."
            }
          },
          {
            "name": "dryRun",
            "in": "query",
            "description": "true validates and compiles the change and returns the plan without committing it.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "force",
            "in": "query",
            "description": "true overrides the admin host's lock-out guard, for a change that would remove the caller's own way in.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "message",
            "in": "query",
            "description": "A note stored with the revision this change makes (at most 1000 characters).",
            "schema": {
              "maxLength": 1000,
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RuleSetConfig"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConfigCommandResult"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "412": {
            "description": "The If-Match tag is stale (or weak): what you read is no longer current. The problem says what is.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "415": {
            "description": "The body is not in a type this operation takes.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "422": {
            "description": "The request is well-formed but can't be applied; errors lists why, with a pointer into the body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "428": {
            "description": "The change needs an If-Match header with the ETag of what you read.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `config:write` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "Either an unexpected error (the trace ID finds it in the server log), or code config.publishFailed: the change was stored (revision, and the new version in version and the ETag header) but could not go live; the previous config still serves.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "config:write"
      },
      "delete": {
        "tags": [
          "Rule sets"
        ],
        "summary": "Delete a rule set",
        "description": "Refused (422, relatedErrors) while a host still resolves to it (named by the host, its profile or the global defaults) or a route names it.\n\nRequires the `config:write` permission.",
        "operationId": "DeleteRuleSet",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "examples": [
                "rs_0A1b2C3d4E5f6G7h8I9j0K"
              ],
              "pattern": "^rs_[0-9A-Za-z]{22}$",
              "type": "string",
              "description": "A rule set: an ordered list of path, method and user-agent rules, which hosts and routes name. The built-in presets\n            have reserved IDs."
            }
          },
          {
            "name": "dryRun",
            "in": "query",
            "description": "true validates and compiles the change and returns the plan without committing it.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "force",
            "in": "query",
            "description": "true overrides the admin host's lock-out guard, for a change that would remove the caller's own way in.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "message",
            "in": "query",
            "description": "A note stored with the revision this change makes (at most 1000 characters).",
            "schema": {
              "maxLength": 1000,
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConfigCommandResult"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "412": {
            "description": "The If-Match tag is stale (or weak): what you read is no longer current. The problem says what is.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "422": {
            "description": "The request is well-formed but can't be applied; errors lists why, with a pointer into the body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "428": {
            "description": "The change needs an If-Match header with the ETag of what you read.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `config:write` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "Either an unexpected error (the trace ID finds it in the server log), or code config.publishFailed: the change was stored (revision, and the new version in version and the ETag header) but could not go live; the previous config still serves.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "config:write"
      }
    },
    "/api/v1/auth-providers": {
      "get": {
        "tags": [
          "Auth providers"
        ],
        "summary": "List auth providers",
        "description": "Requires the `config:read` permission.",
        "operationId": "ListAuthProviders",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "description": "How many items a page holds, 1 to 200 (default 50).",
            "schema": {
              "maximum": 200,
              "minimum": 1,
              "type": "integer",
              "default": 50
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "The nextCursor of the previous page, to continue after it. An opaque string only the API that issued it understands.",
            "schema": {
              "maxLength": 96,
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PageOfAuthProviderConfig"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `config:read` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "config:read"
      },
      "post": {
        "tags": [
          "Auth providers"
        ],
        "summary": "Create an auth provider",
        "description": "A forward-auth endpoint: every request to a host or route that names the provider is checked against it first (authentik, Authelia, oauth2-proxy or any other). The preset (authentik, authelia, oauth2Proxy) fills in the endpoint's path when the URL is a bare address, the identity headers its answer carries, whether a remembered allow covers one path or the whole host, and oauth2-proxy's sign-in redirect; a field that is set wins. A URL with credentials, a header the proxy owns and a sign-in URL that isn't a path on the host or an absolute http(s) URL are refused (422).\n\nRequires the `config:write` permission.",
        "operationId": "CreateAuthProvider",
        "parameters": [
          {
            "name": "dryRun",
            "in": "query",
            "description": "true validates and compiles the change and returns the plan without committing it.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "force",
            "in": "query",
            "description": "true overrides the admin host's lock-out guard, for a change that would remove the caller's own way in.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "message",
            "in": "query",
            "description": "A note stored with the revision this change makes (at most 1000 characters).",
            "schema": {
              "maxLength": 1000,
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AuthProviderConfig"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConfigCommandResult"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "409": {
            "description": "It clashes with what exists, or with something in progress.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "415": {
            "description": "The body is not in a type this operation takes.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "422": {
            "description": "The request is well-formed but can't be applied; errors lists why, with a pointer into the body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `config:write` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "Either an unexpected error (the trace ID finds it in the server log), or code config.publishFailed: the change was stored (revision, and the new version in version and the ETag header) but could not go live; the previous config still serves.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "config:write"
      }
    },
    "/api/v1/auth-providers/{id}": {
      "get": {
        "tags": [
          "Auth providers"
        ],
        "summary": "Get an auth provider",
        "description": "Requires the `config:read` permission.",
        "operationId": "GetAuthProvider",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "examples": [
                "authp_0A1b2C3d4E5f6G7h8I9j0K"
              ],
              "pattern": "^authp_[0-9A-Za-z]{22}$",
              "type": "string",
              "description": "An authentication provider: a forward-auth endpoint (authentik, Authelia, oauth2-proxy or any other) that hosts and routes\n            name to ask who a request is before it is forwarded."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthProviderConfig"
                }
              }
            }
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `config:read` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "config:read"
      },
      "put": {
        "tags": [
          "Auth providers"
        ],
        "summary": "Replace an auth provider",
        "description": "A change to what the endpoint is asked or how its answer is read starts the provider with an empty decision cache; a rename or new notes keep it.\n\nRequires the `config:write` permission.",
        "operationId": "ReplaceAuthProvider",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "examples": [
                "authp_0A1b2C3d4E5f6G7h8I9j0K"
              ],
              "pattern": "^authp_[0-9A-Za-z]{22}$",
              "type": "string",
              "description": "An authentication provider: a forward-auth endpoint (authentik, Authelia, oauth2-proxy or any other) that hosts and routes\n            name to ask who a request is before it is forwarded."
            }
          },
          {
            "name": "dryRun",
            "in": "query",
            "description": "true validates and compiles the change and returns the plan without committing it.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "force",
            "in": "query",
            "description": "true overrides the admin host's lock-out guard, for a change that would remove the caller's own way in.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "message",
            "in": "query",
            "description": "A note stored with the revision this change makes (at most 1000 characters).",
            "schema": {
              "maxLength": 1000,
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AuthProviderConfig"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConfigCommandResult"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "412": {
            "description": "The If-Match tag is stale (or weak): what you read is no longer current. The problem says what is.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "415": {
            "description": "The body is not in a type this operation takes.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "422": {
            "description": "The request is well-formed but can't be applied; errors lists why, with a pointer into the body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "428": {
            "description": "The change needs an If-Match header with the ETag of what you read.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `config:write` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "Either an unexpected error (the trace ID finds it in the server log), or code config.publishFailed: the change was stored (revision, and the new version in version and the ETag header) but could not go live; the previous config still serves.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "config:write"
      },
      "delete": {
        "tags": [
          "Auth providers"
        ],
        "summary": "Delete an auth provider",
        "description": "Refused (422, relatedErrors) while a host still resolves to it (named by the host, its profile or the global defaults) or a route names it.\n\nRequires the `config:write` permission.",
        "operationId": "DeleteAuthProvider",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "examples": [
                "authp_0A1b2C3d4E5f6G7h8I9j0K"
              ],
              "pattern": "^authp_[0-9A-Za-z]{22}$",
              "type": "string",
              "description": "An authentication provider: a forward-auth endpoint (authentik, Authelia, oauth2-proxy or any other) that hosts and routes\n            name to ask who a request is before it is forwarded."
            }
          },
          {
            "name": "dryRun",
            "in": "query",
            "description": "true validates and compiles the change and returns the plan without committing it.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "force",
            "in": "query",
            "description": "true overrides the admin host's lock-out guard, for a change that would remove the caller's own way in.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "message",
            "in": "query",
            "description": "A note stored with the revision this change makes (at most 1000 characters).",
            "schema": {
              "maxLength": 1000,
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConfigCommandResult"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "412": {
            "description": "The If-Match tag is stale (or weak): what you read is no longer current. The problem says what is.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "422": {
            "description": "The request is well-formed but can't be applied; errors lists why, with a pointer into the body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "428": {
            "description": "The change needs an If-Match header with the ETag of what you read.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `config:write` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "Either an unexpected error (the trace ID finds it in the server log), or code config.publishFailed: the change was stored (revision, and the new version in version and the ETag header) but could not go live; the previous config still serves.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "config:write"
      }
    },
    "/api/v1/profiles": {
      "get": {
        "tags": [
          "Profiles"
        ],
        "summary": "List profiles",
        "description": "Requires the `config:read` permission.",
        "operationId": "ListProfiles",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "description": "How many items a page holds, 1 to 200 (default 50).",
            "schema": {
              "maximum": 200,
              "minimum": 1,
              "type": "integer",
              "default": 50
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "The nextCursor of the previous page, to continue after it. An opaque string only the API that issued it understands.",
            "schema": {
              "maxLength": 96,
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PageOfProfileConfig"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `config:read` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "config:read"
      },
      "post": {
        "tags": [
          "Profiles"
        ],
        "summary": "Create a profile",
        "description": "Requires the `config:write` permission.",
        "operationId": "CreateProfile",
        "parameters": [
          {
            "name": "dryRun",
            "in": "query",
            "description": "true validates and compiles the change and returns the plan without committing it.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "force",
            "in": "query",
            "description": "true overrides the admin host's lock-out guard, for a change that would remove the caller's own way in.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "message",
            "in": "query",
            "description": "A note stored with the revision this change makes (at most 1000 characters).",
            "schema": {
              "maxLength": 1000,
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ProfileConfig"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConfigCommandResult"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "409": {
            "description": "It clashes with what exists, or with something in progress.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "415": {
            "description": "The body is not in a type this operation takes.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "422": {
            "description": "The request is well-formed but can't be applied; errors lists why, with a pointer into the body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `config:write` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "Either an unexpected error (the trace ID finds it in the server log), or code config.publishFailed: the change was stored (revision, and the new version in version and the ETag header) but could not go live; the previous config still serves.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "config:write"
      }
    },
    "/api/v1/profiles/{id}": {
      "get": {
        "tags": [
          "Profiles"
        ],
        "summary": "Get a profile",
        "description": "Requires the `config:read` permission.",
        "operationId": "GetProfile",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "examples": [
                "prof_0A1b2C3d4E5f6G7h8I9j0K"
              ],
              "pattern": "^prof_[0-9A-Za-z]{22}$",
              "type": "string",
              "description": "A named preset of host-settings overrides."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProfileConfig"
                }
              }
            }
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `config:read` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "config:read"
      },
      "put": {
        "tags": [
          "Profiles"
        ],
        "summary": "Replace a profile",
        "description": "Requires the `config:write` permission.",
        "operationId": "ReplaceProfile",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "examples": [
                "prof_0A1b2C3d4E5f6G7h8I9j0K"
              ],
              "pattern": "^prof_[0-9A-Za-z]{22}$",
              "type": "string",
              "description": "A named preset of host-settings overrides."
            }
          },
          {
            "name": "dryRun",
            "in": "query",
            "description": "true validates and compiles the change and returns the plan without committing it.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "force",
            "in": "query",
            "description": "true overrides the admin host's lock-out guard, for a change that would remove the caller's own way in.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "message",
            "in": "query",
            "description": "A note stored with the revision this change makes (at most 1000 characters).",
            "schema": {
              "maxLength": 1000,
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ProfileConfig"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConfigCommandResult"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "412": {
            "description": "The If-Match tag is stale (or weak): what you read is no longer current. The problem says what is.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "415": {
            "description": "The body is not in a type this operation takes.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "422": {
            "description": "The request is well-formed but can't be applied; errors lists why, with a pointer into the body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "428": {
            "description": "The change needs an If-Match header with the ETag of what you read.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `config:write` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "Either an unexpected error (the trace ID finds it in the server log), or code config.publishFailed: the change was stored (revision, and the new version in version and the ETag header) but could not go live; the previous config still serves.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "config:write"
      },
      "delete": {
        "tags": [
          "Profiles"
        ],
        "summary": "Delete a profile",
        "description": "Refused (422, relatedErrors) while a host still uses it.\n\nRequires the `config:write` permission.",
        "operationId": "DeleteProfile",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "examples": [
                "prof_0A1b2C3d4E5f6G7h8I9j0K"
              ],
              "pattern": "^prof_[0-9A-Za-z]{22}$",
              "type": "string",
              "description": "A named preset of host-settings overrides."
            }
          },
          {
            "name": "dryRun",
            "in": "query",
            "description": "true validates and compiles the change and returns the plan without committing it.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "force",
            "in": "query",
            "description": "true overrides the admin host's lock-out guard, for a change that would remove the caller's own way in.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "message",
            "in": "query",
            "description": "A note stored with the revision this change makes (at most 1000 characters).",
            "schema": {
              "maxLength": 1000,
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConfigCommandResult"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "412": {
            "description": "The If-Match tag is stale (or weak): what you read is no longer current. The problem says what is.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "422": {
            "description": "The request is well-formed but can't be applied; errors lists why, with a pointer into the body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "428": {
            "description": "The change needs an If-Match header with the ETag of what you read.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `config:write` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "Either an unexpected error (the trace ID finds it in the server log), or code config.publishFailed: the change was stored (revision, and the new version in version and the ETag header) but could not go live; the previous config still serves.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "config:write"
      }
    },
    "/api/v1/drafts": {
      "get": {
        "tags": [
          "Drafts"
        ],
        "summary": "The caller's open draft",
        "description": "A list of at most one: the caller's open draft (a token's is its owner's), each entry marked Ready or ChangedElsewhere against the latest revision. Empty when there is none.\n\nRequires the `config:write` permission.",
        "operationId": "ListDrafts",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "description": "How many items a page holds, 1 to 200 (default 50).",
            "schema": {
              "maximum": 200,
              "minimum": 1,
              "type": "integer",
              "default": 50
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "The nextCursor of the previous page, to continue after it. An opaque string only the API that issued it understands.",
            "schema": {
              "maxLength": 96,
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PageOfConfigDraft"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `config:write` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "config:write"
      },
      "post": {
        "tags": [
          "Drafts"
        ],
        "summary": "Open a draft",
        "description": "Starts an empty draft on the latest revision. 409 when the caller has one open already (see Location).\n\nRequires the `config:write` permission.",
        "operationId": "CreateDraft",
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConfigDraft"
                }
              }
            }
          },
          "403": {
            "description": "The caller may not do this.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "409": {
            "description": "It clashes with what exists, or with something in progress.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "503": {
            "description": "The server can't do this right now; try again shortly.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "config:write"
      }
    },
    "/api/v1/drafts/{id}": {
      "get": {
        "tags": [
          "Drafts"
        ],
        "summary": "Get the open draft",
        "description": "The draft with each entry's state against the latest revision. The ETag is the draft's version.\n\nRequires the `config:write` permission.",
        "operationId": "GetDraft",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "examples": [
                "drf_0A1b2C3d4E5f6G7h8I9j0K"
              ],
              "pattern": "^drf_[0-9A-Za-z]{22}$",
              "type": "string",
              "description": "A draft: one user's open change set across pages, applied as one revision (docs/design/dashboard.md \"Drafts (M10)\")."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConfigDraft"
                }
              }
            }
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "410": {
            "description": "Gone",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `config:write` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "config:write"
      },
      "delete": {
        "tags": [
          "Drafts"
        ],
        "summary": "Discard the draft",
        "description": "Drops the draft and the vault values bound to it. Nothing was committed, so nothing is undone.\n\nRequires the `config:write` permission.",
        "operationId": "DiscardDraft",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "examples": [
                "drf_0A1b2C3d4E5f6G7h8I9j0K"
              ],
              "pattern": "^drf_[0-9A-Za-z]{22}$",
              "type": "string",
              "description": "A draft: one user's open change set across pages, applied as one revision (docs/design/dashboard.md \"Drafts (M10)\")."
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `config:write` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "config:write"
      }
    },
    "/api/v1/drafts/{id}/config": {
      "get": {
        "tags": [
          "Drafts"
        ],
        "summary": "The config with the draft applied",
        "description": "The latest revision's document with every entry that is not changed elsewhere applied in order: what a page reads while the draft is open. Versions are the latest revision's.\n\nRequires the `config:write` permission.",
        "operationId": "GetDraftConfig",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "examples": [
                "drf_0A1b2C3d4E5f6G7h8I9j0K"
              ],
              "pattern": "^drf_[0-9A-Za-z]{22}$",
              "type": "string",
              "description": "A draft: one user's open change set across pages, applied as one revision (docs/design/dashboard.md \"Drafts (M10)\")."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DraftConfig"
                }
              }
            }
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "410": {
            "description": "Gone",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `config:write` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "config:write"
      }
    },
    "/api/v1/drafts/{id}/entries": {
      "post": {
        "tags": [
          "Drafts"
        ],
        "summary": "Add or replace entries",
        "description": "The body is a list of object commands (upsert or delete of a host, upstream, profile, rate limit, access list, rule set or auth provider, or the settings), each with the version it was made against, like the object endpoints' If-Match. Each is checked as a dry run on the latest revision with the draft's other entries applied, with the same errors a live command gives, and replaces the draft's earlier entry for its object; one that leaves its object as it is takes the entry out, and deleting an object the draft creates removes its entry. If one is refused (422, 412 or 404 like the live command) none is added. A password in an access list goes to the vault as a pending value bound to the draft; it is never stored in the draft or returned.\n\nRequires the `config:write` permission.",
        "operationId": "AddDraftEntries",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "examples": [
                "drf_0A1b2C3d4E5f6G7h8I9j0K"
              ],
              "pattern": "^drf_[0-9A-Za-z]{22}$",
              "type": "string",
              "description": "A draft: one user's open change set across pages, applied as one revision (docs/design/dashboard.md \"Drafts (M10)\")."
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AddDraftEntriesRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConfigDraft"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller may not do this.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "410": {
            "description": "Gone",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "412": {
            "description": "The If-Match tag is stale (or weak): what you read is no longer current. The problem says what is.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "415": {
            "description": "The body is not in a type this operation takes.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "422": {
            "description": "The request is well-formed but can't be applied; errors lists why, with a pointer into the body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "503": {
            "description": "The server can't do this right now; try again shortly.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "config:write"
      }
    },
    "/api/v1/drafts/{id}/entries/{number}": {
      "delete": {
        "tags": [
          "Drafts"
        ],
        "summary": "Remove an entry",
        "description": "Takes one entry out of the draft, whatever its state: this is also how an entry that is changed elsewhere is reloaded (read the object again and add it afresh). The ETag of the answer is the draft's new version.\n\nRequires the `config:write` permission.",
        "operationId": "RemoveDraftEntry",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "examples": [
                "drf_0A1b2C3d4E5f6G7h8I9j0K"
              ],
              "pattern": "^drf_[0-9A-Za-z]{22}$",
              "type": "string",
              "description": "A draft: one user's open change set across pages, applied as one revision (docs/design/dashboard.md \"Drafts (M10)\")."
            }
          },
          {
            "name": "number",
            "in": "path",
            "required": true,
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "410": {
            "description": "Gone",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `config:write` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "config:write"
      }
    },
    "/api/v1/drafts/{id}/review": {
      "post": {
        "tags": [
          "Drafts"
        ],
        "summary": "Review the draft as a dry run",
        "description": "Replays the entries on the latest revision and dry-runs them as one change: the diff, the compiler's warnings, the admin lock-out guard (?force=true overrides it) and the errors of the whole set. The answer's reviewToken (also its ETag) is what apply must send back as If-Match to commit exactly this: it covers what the draft would do, so a change to the draft, to an object it writes or refers to, or to what the compiler says about it makes it stale, while an unrelated commit does not. A draft with entries that are changed elsewhere has no token (409 draft.blocked when every entry is); the answer shows the rest. Commits and audits nothing.\n\nRequires the `config:write` permission.",
        "operationId": "ReviewDraft",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "examples": [
                "drf_0A1b2C3d4E5f6G7h8I9j0K"
              ],
              "pattern": "^drf_[0-9A-Za-z]{22}$",
              "type": "string",
              "description": "A draft: one user's open change set across pages, applied as one revision (docs/design/dashboard.md \"Drafts (M10)\")."
            }
          },
          {
            "name": "force",
            "in": "query",
            "description": "true overrides the admin host's lock-out guard, for a change that would remove the caller's own way in.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DraftOutcome"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "409": {
            "description": "It clashes with what exists, or with something in progress.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "410": {
            "description": "Gone",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "422": {
            "description": "The request is well-formed but can't be applied; errors lists why, with a pointer into the body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "503": {
            "description": "The server can't do this right now; try again shortly.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `config:write` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "config:write"
      }
    },
    "/api/v1/drafts/{id}/apply": {
      "post": {
        "tags": [
          "Drafts"
        ],
        "summary": "Apply the draft as one revision",
        "description": "Commits the whole draft as ONE revision, audited once with every entry listed, or nothing: what is confirmed is exactly what is applied. Needs If-Match with the review's token (428 without it, with the fresh review in the problem's review). Under the writer's lock the draft is replayed on the current head; unless that is what the review showed, 412 draft.changedSinceReview commits nothing and returns the fresh review (and its token) in the problem's review. Any entry that is changed elsewhere, or depends on one that is, refuses the whole draft (409 draft.blocked, errors naming each); remove or reload them and review again. ?message= is the revision's note (it defaults to a summary of the entries). ?dryRun=true is the review. Undo of the revision undoes the whole draft.\n\nRequires the `config:write` permission.",
        "operationId": "ApplyDraft",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "examples": [
                "drf_0A1b2C3d4E5f6G7h8I9j0K"
              ],
              "pattern": "^drf_[0-9A-Za-z]{22}$",
              "type": "string",
              "description": "A draft: one user's open change set across pages, applied as one revision (docs/design/dashboard.md \"Drafts (M10)\")."
            }
          },
          {
            "name": "dryRun",
            "in": "query",
            "description": "true validates and compiles the change and returns the plan without committing it.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "force",
            "in": "query",
            "description": "true overrides the admin host's lock-out guard, for a change that would remove the caller's own way in.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "message",
            "in": "query",
            "description": "A note stored with the revision this change makes (at most 1000 characters).",
            "schema": {
              "maxLength": 1000,
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DraftOutcome"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "409": {
            "description": "It clashes with what exists, or with something in progress.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "410": {
            "description": "Gone",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "412": {
            "description": "The If-Match tag is stale (or weak): what you read is no longer current. The problem says what is.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "422": {
            "description": "The request is well-formed but can't be applied; errors lists why, with a pointer into the body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "428": {
            "description": "The change needs an If-Match header with the ETag of what you read.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "503": {
            "description": "The server can't do this right now; try again shortly.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `config:write` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "Either an unexpected error (the trace ID finds it in the server log), or code config.publishFailed: the change was stored (revision, and the new version in version and the ETag header) but could not go live; the previous config still serves.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "config:write"
      }
    },
    "/api/v1/revisions": {
      "get": {
        "tags": [
          "Revisions"
        ],
        "summary": "List revisions",
        "description": "Newest first.\n\nRequires the `config:read` permission.",
        "operationId": "ListRevisions",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "description": "How many items a page holds, 1 to 200 (default 50).",
            "schema": {
              "maximum": 200,
              "minimum": 1,
              "type": "integer",
              "default": 50
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "The nextCursor of the previous page, to continue after it. An opaque string only the API that issued it understands.",
            "schema": {
              "maxLength": 96,
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PageOfRevisionSummary"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `config:read` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "config:read"
      }
    },
    "/api/v1/revisions/diff": {
      "get": {
        "tags": [
          "Revisions"
        ],
        "summary": "What changed between two revisions",
        "description": "The structural diff of the two revisions' documents, in the form dry runs report.\n\nRequires the `config:read` permission.",
        "operationId": "DiffRevisions",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "description": "The earlier revision (rev_...).",
            "schema": {
              "examples": [
                "rev_0A1b2C3d4E5f6G7h8I9j0K"
              ],
              "pattern": "^rev_[0-9A-Za-z]{22}$",
              "type": "string",
              "description": "A committed config revision."
            }
          },
          {
            "name": "to",
            "in": "query",
            "description": "The later revision (rev_...).",
            "schema": {
              "examples": [
                "rev_0A1b2C3d4E5f6G7h8I9j0K"
              ],
              "pattern": "^rev_[0-9A-Za-z]{22}$",
              "type": "string",
              "description": "A committed config revision."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RevisionDiff"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `config:read` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "config:read"
      }
    },
    "/api/v1/revisions/{id}": {
      "get": {
        "tags": [
          "Revisions"
        ],
        "summary": "Get a revision with its document",
        "description": "Requires the `config:read` permission.",
        "operationId": "GetRevision",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "examples": [
                "rev_0A1b2C3d4E5f6G7h8I9j0K"
              ],
              "pattern": "^rev_[0-9A-Za-z]{22}$",
              "type": "string",
              "description": "A committed config revision."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RevisionDetail"
                }
              }
            }
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `config:read` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "config:read"
      }
    },
    "/api/v1/revisions/{id}/rollback": {
      "post": {
        "tags": [
          "Revisions"
        ],
        "summary": "Restore an earlier revision as a new one",
        "description": "Needs If-Match with the latest revision's ID, as the ETag of GET /config (428 without it, 412 when it is no longer the latest). Supports ?dryRun=true.\n\nRequires the `config:write` permission.",
        "operationId": "RollbackToRevision",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "examples": [
                "rev_0A1b2C3d4E5f6G7h8I9j0K"
              ],
              "pattern": "^rev_[0-9A-Za-z]{22}$",
              "type": "string",
              "description": "A committed config revision."
            }
          },
          {
            "name": "dryRun",
            "in": "query",
            "description": "true validates and compiles the change and returns the plan without committing it.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "force",
            "in": "query",
            "description": "true overrides the admin host's lock-out guard, for a change that would remove the caller's own way in.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "message",
            "in": "query",
            "description": "A note stored with the revision this change makes (at most 1000 characters).",
            "schema": {
              "maxLength": 1000,
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConfigCommandResult"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "412": {
            "description": "The If-Match tag is stale (or weak): what you read is no longer current. The problem says what is.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "422": {
            "description": "The request is well-formed but can't be applied; errors lists why, with a pointer into the body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "428": {
            "description": "The change needs an If-Match header with the ETag of what you read.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `config:write` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "Either an unexpected error (the trace ID finds it in the server log), or code config.publishFailed: the change was stored (revision, and the new version in version and the ETag header) but could not go live; the previous config still serves.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "config:write"
      }
    },
    "/api/v1/certificates": {
      "get": {
        "tags": [
          "Certificates"
        ],
        "summary": "List certificates",
        "description": "Public data only. The filters are all optional and all must match.\n\nRequires the `certificates:read` permission.",
        "operationId": "ListCertificates",
        "parameters": [
          {
            "name": "source",
            "in": "query",
            "description": "Only certificates from this source.",
            "schema": {
              "enum": [
                "acme",
                "uploaded",
                "self_signed",
                "local_ca",
                "local_ca_root"
              ],
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "description": "Only certificates in this status.",
            "schema": {
              "enum": [
                "pending",
                "active",
                "failed"
              ],
              "type": "string"
            }
          },
          {
            "name": "name",
            "in": "query",
            "description": "Only the certificates that cover this host name.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "How many items a page holds, 1 to 200 (default 50).",
            "schema": {
              "maximum": 200,
              "minimum": 1,
              "type": "integer",
              "default": 50
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "The nextCursor of the previous page, to continue after it. An opaque string only the API that issued it understands.",
            "schema": {
              "maxLength": 96,
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PageOfCertificateSummary"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `certificates:read` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "certificates:read"
      },
      "post": {
        "tags": [
          "Certificates"
        ],
        "summary": "Upload a certificate with its key (PEM or PFX)",
        "description": "multipart/form-data. Parts, in this order: 'password' (optional), 'label' (optional), then 'file': the PEM (PKCS#8 key) or PFX, at most 1 MiB. The file is streamed into the vault; the key is never returned.\n\nRequires the `certificates:write` permission.",
        "operationId": "UploadCertificate",
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/UploadForm"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CertificateOperationResult"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "413": {
            "description": "The body is larger than this operation takes.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "415": {
            "description": "The body is not in a type this operation takes.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "422": {
            "description": "The request is well-formed but can't be applied; errors lists why, with a pointer into the body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `certificates:write` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "certificates:write"
      }
    },
    "/api/v1/certificates/{id}": {
      "get": {
        "tags": [
          "Certificates"
        ],
        "summary": "Get a certificate with its public chain",
        "description": "Requires the `certificates:read` permission.",
        "operationId": "GetCertificate",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "examples": [
                "crt_0A1b2C3d4E5f6G7h8I9j0K"
              ],
              "pattern": "^crt_[0-9A-Za-z]{22}$",
              "type": "string",
              "description": "A certificate. Hosts bind to one; the key material stays in the vault."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CertificateDetail"
                }
              }
            }
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `certificates:read` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "certificates:read"
      },
      "put": {
        "tags": [
          "Certificates"
        ],
        "summary": "Replace an uploaded certificate under the same ID",
        "description": "Like the upload, for a renewed certificate: key and chain swap together, so bindings that name the certificate keep working.\n\nRequires the `certificates:write` permission.",
        "operationId": "ReplaceUploadedCertificate",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "examples": [
                "crt_0A1b2C3d4E5f6G7h8I9j0K"
              ],
              "pattern": "^crt_[0-9A-Za-z]{22}$",
              "type": "string",
              "description": "A certificate. Hosts bind to one; the key material stays in the vault."
            }
          }
        ],
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/UploadForm"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CertificateOperationResult"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "413": {
            "description": "The body is larger than this operation takes.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "415": {
            "description": "The body is not in a type this operation takes.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "422": {
            "description": "The request is well-formed but can't be applied; errors lists why, with a pointer into the body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `certificates:write` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "certificates:write"
      },
      "delete": {
        "tags": [
          "Certificates"
        ],
        "summary": "Delete a certificate and its key",
        "description": "Refused (422) while a host still binds it specifically. 204 when it is gone; 200 with the result when there are warnings to report (a vault key that could not be removed).\n\nRequires the `certificates:write` permission.",
        "operationId": "DeleteCertificate",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "examples": [
                "crt_0A1b2C3d4E5f6G7h8I9j0K"
              ],
              "pattern": "^crt_[0-9A-Za-z]{22}$",
              "type": "string",
              "description": "A certificate. Hosts bind to one; the key material stays in the vault."
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CertificateOperationResult"
                }
              }
            }
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "422": {
            "description": "The request is well-formed but can't be applied; errors lists why, with a pointer into the body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `certificates:write` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "certificates:write"
      }
    },
    "/api/v1/certificates/self-signed": {
      "post": {
        "tags": [
          "Certificates"
        ],
        "summary": "Issue a self-signed certificate",
        "description": "Requires the `certificates:write` permission.",
        "operationId": "IssueSelfSignedCertificate",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SelfSignedCertificateRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CertificateOperationResult"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "415": {
            "description": "The body is not in a type this operation takes.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "422": {
            "description": "The request is well-formed but can't be applied; errors lists why, with a pointer into the body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `certificates:write` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "certificates:write"
      }
    },
    "/api/v1/certificates/local-ca": {
      "post": {
        "tags": [
          "Certificates"
        ],
        "summary": "Create the local CA root",
        "description": "There is one local CA: asking again returns the existing root. The body is optional.\n\nRequires the `certificates:write` permission.",
        "operationId": "CreateLocalCa",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LocalCaRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CertificateOperationResult"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "415": {
            "description": "The body is not in a type this operation takes.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "422": {
            "description": "The request is well-formed but can't be applied; errors lists why, with a pointer into the body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `certificates:write` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "certificates:write"
      },
      "get": {
        "tags": [
          "Certificates"
        ],
        "summary": "The local CA: its root, what it is limited to, and the roots a rotation left",
        "description": "Requires the `certificates:read` permission.",
        "operationId": "GetLocalCa",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LocalCaStatus"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `certificates:read` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "certificates:read"
      }
    },
    "/api/v1/certificates/local-ca/rotate": {
      "post": {
        "tags": [
          "Certificates"
        ],
        "summary": "Replace the local CA root",
        "description": "Makes a new root (limited to the private-use domains, the private ranges and the names in the body) and destroys the old root's key in the same step. Devices must trust the new root before what it signs works for them; what the old root signed keeps working, and the old root stays downloadable, until it is retired (DELETE its certificate once nothing it signed is left). The admin listener's certificate is issued again at once. The body is optional.\n\nRequires the `certificates:write` permission.",
        "operationId": "RotateLocalCa",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LocalCaRotateRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CertificateOperationResult"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "415": {
            "description": "The body is not in a type this operation takes.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "422": {
            "description": "The request is well-formed but can't be applied; errors lists why, with a pointer into the body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `certificates:write` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "certificates:write"
      }
    },
    "/api/v1/certificates/local-ca/reissue": {
      "post": {
        "tags": [
          "Certificates"
        ],
        "summary": "Re-issue what a retiring local CA root signed",
        "description": "Issues every certificate a root replaced by a rotation signed again, from the current root, under the same IDs. Certificates whose names the new root does not permit are left alone and listed in failures.\n\nRequires the `certificates:write` permission.",
        "operationId": "ReissueLocalCaCertificates",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LocalCaReissueResult"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `certificates:write` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "certificates:write"
      }
    },
    "/api/v1/certificates/local-ca/leaf": {
      "post": {
        "tags": [
          "Certificates"
        ],
        "summary": "Issue a certificate from the local CA",
        "description": "Requires the `certificates:write` permission.",
        "operationId": "IssueLocalCaCertificate",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LocalCaLeafRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CertificateOperationResult"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "415": {
            "description": "The body is not in a type this operation takes.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "422": {
            "description": "The request is well-formed but can't be applied; errors lists why, with a pointer into the body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `certificates:write` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "certificates:write"
      }
    },
    "/api/v1/certificates/{id}/reissue": {
      "post": {
        "tags": [
          "Certificates"
        ],
        "summary": "Re-issue a self-signed or local-CA certificate with a new key",
        "description": "Same ID and names, so bindings survive. The body is optional.\n\nRequires the `certificates:write` permission.",
        "operationId": "ReissueCertificate",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "examples": [
                "crt_0A1b2C3d4E5f6G7h8I9j0K"
              ],
              "pattern": "^crt_[0-9A-Za-z]{22}$",
              "type": "string",
              "description": "A certificate. Hosts bind to one; the key material stays in the vault."
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ReissueRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CertificateOperationResult"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "415": {
            "description": "The body is not in a type this operation takes.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "422": {
            "description": "The request is well-formed but can't be applied; errors lists why, with a pointer into the body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `certificates:write` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "certificates:write"
      }
    },
    "/api/v1/certificates/acme": {
      "post": {
        "tags": [
          "Certificates"
        ],
        "summary": "Request a certificate from an ACME CA",
        "description": "Runs the first issuance to the end and answers when it is issued or has failed (502, and the scheduler retries; the problem's instance is the certificate). The issuance is not tied to the request: a client that gives up does not cancel it, and GET /certificates/{id}/order shows its progress. A DNS-01 order waits for propagation and can outlast a typical client timeout, so give the request minutes, or poll the order instead.\n\nRequires the `certificates:write` permission.",
        "operationId": "RequestAcmeCertificate",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CertificateIssueRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IssuanceResult"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "409": {
            "description": "It clashes with what exists, or with something in progress.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "415": {
            "description": "The body is not in a type this operation takes.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "422": {
            "description": "The request is well-formed but can't be applied; errors lists why, with a pointer into the body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "502": {
            "description": "The CA refused or could not be reached; nothing here is wrong with the request.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `certificates:write` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "certificates:write"
      }
    },
    "/api/v1/certificates/{id}/renew": {
      "post": {
        "tags": [
          "Certificates"
        ],
        "summary": "Renew an ACME certificate now",
        "description": "Like the request: runs to the end whether or not the client waits, and answers synchronously when it finishes; GET /certificates/{id}/order shows progress.\n\nRequires the `certificates:write` permission.",
        "operationId": "RenewCertificateNow",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "examples": [
                "crt_0A1b2C3d4E5f6G7h8I9j0K"
              ],
              "pattern": "^crt_[0-9A-Za-z]{22}$",
              "type": "string",
              "description": "A certificate. Hosts bind to one; the key material stays in the vault."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IssuanceResult"
                }
              }
            }
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "409": {
            "description": "It clashes with what exists, or with something in progress.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "422": {
            "description": "The request is well-formed but can't be applied; errors lists why, with a pointer into the body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "502": {
            "description": "The CA refused or could not be reached; nothing here is wrong with the request.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `certificates:write` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "certificates:write"
      }
    },
    "/api/v1/certificates/{id}/order": {
      "get": {
        "tags": [
          "Certificates"
        ],
        "summary": "The ACME order and renewal state of a certificate",
        "description": "Requires the `certificates:read` permission.",
        "operationId": "GetCertificateOrder",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "examples": [
                "crt_0A1b2C3d4E5f6G7h8I9j0K"
              ],
              "pattern": "^crt_[0-9A-Za-z]{22}$",
              "type": "string",
              "description": "A certificate. Hosts bind to one; the key material stays in the vault."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AcmeOrderSummary"
                }
              }
            }
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `certificates:read` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "certificates:read"
      }
    },
    "/api/v1/acme-accounts": {
      "get": {
        "tags": [
          "ACME accounts"
        ],
        "summary": "List ACME accounts",
        "description": "Requires the `providers:read` permission.",
        "operationId": "ListAcmeAccounts",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "description": "How many items a page holds, 1 to 200 (default 50).",
            "schema": {
              "maximum": 200,
              "minimum": 1,
              "type": "integer",
              "default": 50
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "The nextCursor of the previous page, to continue after it. An opaque string only the API that issued it understands.",
            "schema": {
              "maxLength": 96,
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PageOfAcmeAccountSummary"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `providers:read` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "providers:read"
      },
      "post": {
        "tags": [
          "ACME accounts"
        ],
        "summary": "Register an ACME account",
        "description": "The vault creates the account key. termsOfServiceAgreed records that the operator accepted the CA's terms; most CAs refuse an account without it. For CAs with external account binding give eabKid and eabHmacKey (base64url, as the CA shows it): the key goes into the vault and is never returned.\n\nRequires the `providers:write` permission.",
        "operationId": "CreateAcmeAccount",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateAcmeAccountBody"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AcmeAccountSummary"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "415": {
            "description": "The body is not in a type this operation takes.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "422": {
            "description": "The request is well-formed but can't be applied; errors lists why, with a pointer into the body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "502": {
            "description": "The CA refused or could not be reached; nothing here is wrong with the request.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `providers:write` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "providers:write"
      }
    },
    "/api/v1/acme-accounts/{id}": {
      "get": {
        "tags": [
          "ACME accounts"
        ],
        "summary": "Get an ACME account",
        "description": "Requires the `providers:read` permission.",
        "operationId": "GetAcmeAccount",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "examples": [
                "acct_0A1b2C3d4E5f6G7h8I9j0K"
              ],
              "pattern": "^acct_[0-9A-Za-z]{22}$",
              "type": "string",
              "description": "An ACME account. Its account key lives in the vault under this ID."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AcmeAccountSummary"
                }
              }
            }
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `providers:read` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "providers:read"
      },
      "delete": {
        "tags": [
          "ACME accounts"
        ],
        "summary": "Deactivate an ACME account",
        "description": "Final (RFC 8555): deactivates the account at the CA and deletes its key. Refused (422) while certificates are ordered with it.\n\nRequires the `providers:write` permission.",
        "operationId": "DeactivateAcmeAccount",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "examples": [
                "acct_0A1b2C3d4E5f6G7h8I9j0K"
              ],
              "pattern": "^acct_[0-9A-Za-z]{22}$",
              "type": "string",
              "description": "An ACME account. Its account key lives in the vault under this ID."
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "422": {
            "description": "The request is well-formed but can't be applied; errors lists why, with a pointer into the body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "502": {
            "description": "The CA refused or could not be reached; nothing here is wrong with the request.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `providers:write` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "providers:write"
      }
    },
    "/api/v1/dns-providers": {
      "get": {
        "tags": [
          "DNS providers"
        ],
        "summary": "List DNS providers",
        "description": "Requires the `providers:read` permission.",
        "operationId": "ListDnsProviders",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "description": "How many items a page holds, 1 to 200 (default 50).",
            "schema": {
              "maximum": 200,
              "minimum": 1,
              "type": "integer",
              "default": 50
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "The nextCursor of the previous page, to continue after it. An opaque string only the API that issued it understands.",
            "schema": {
              "maxLength": 96,
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PageOfDnsProviderSummary"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `providers:read` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "providers:read"
      },
      "post": {
        "tags": [
          "DNS providers"
        ],
        "summary": "Add a DNS provider",
        "description": "The credential goes into the vault, usable only against the provider's own API host, and is never returned.\n\nRequires the `providers:write` permission.",
        "operationId": "CreateDnsProvider",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateDnsProviderBody"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DnsProviderSummary"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "415": {
            "description": "The body is not in a type this operation takes.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "422": {
            "description": "The request is well-formed but can't be applied; errors lists why, with a pointer into the body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `providers:write` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "providers:write"
      }
    },
    "/api/v1/dns-providers/{id}": {
      "get": {
        "tags": [
          "DNS providers"
        ],
        "summary": "Get a DNS provider",
        "description": "Requires the `providers:read` permission.",
        "operationId": "GetDnsProvider",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "examples": [
                "dnsp_0A1b2C3d4E5f6G7h8I9j0K"
              ],
              "pattern": "^dnsp_[0-9A-Za-z]{22}$",
              "type": "string",
              "description": "A configured DNS provider for DNS-01 challenges. Its credentials are a SecretId."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DnsProviderSummary"
                }
              }
            }
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `providers:read` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "providers:read"
      },
      "delete": {
        "tags": [
          "DNS providers"
        ],
        "summary": "Delete a DNS provider and its credential",
        "description": "Refused (422) while certificates are ordered through it.\n\nRequires the `providers:write` permission.",
        "operationId": "DeleteDnsProvider",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "examples": [
                "dnsp_0A1b2C3d4E5f6G7h8I9j0K"
              ],
              "pattern": "^dnsp_[0-9A-Za-z]{22}$",
              "type": "string",
              "description": "A configured DNS provider for DNS-01 challenges. Its credentials are a SecretId."
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "422": {
            "description": "The request is well-formed but can't be applied; errors lists why, with a pointer into the body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `providers:write` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "providers:write"
      }
    },
    "/api/v1/dns-providers/{id}/credential": {
      "post": {
        "tags": [
          "DNS providers"
        ],
        "summary": "Replace a DNS provider's credential",
        "description": "The provider keeps its ID, so everything that uses it keeps working. The credential is never returned.\n\nRequires the `providers:write` permission.",
        "operationId": "RotateDnsProviderCredential",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "examples": [
                "dnsp_0A1b2C3d4E5f6G7h8I9j0K"
              ],
              "pattern": "^dnsp_[0-9A-Za-z]{22}$",
              "type": "string",
              "description": "A configured DNS provider for DNS-01 challenges. Its credentials are a SecretId."
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RotateDnsCredentialBody"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DnsProviderSummary"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "415": {
            "description": "The body is not in a type this operation takes.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "422": {
            "description": "The request is well-formed but can't be applied; errors lists why, with a pointer into the body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `providers:write` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "providers:write"
      }
    },
    "/api/v1/dns-providers/{id}/test": {
      "post": {
        "tags": [
          "DNS providers"
        ],
        "summary": "Test a DNS provider's credentials",
        "description": "Asks the provider to check the credential with a read-only call. A bad credential is valid=false, not an error. The test is audited (acme.dns_provider.test) with its verdict only.\n\nRequires the `providers:write` permission.",
        "operationId": "TestDnsProviderCredentials",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "examples": [
                "dnsp_0A1b2C3d4E5f6G7h8I9j0K"
              ],
              "pattern": "^dnsp_[0-9A-Za-z]{22}$",
              "type": "string",
              "description": "A configured DNS provider for DNS-01 challenges. Its credentials are a SecretId."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DnsProviderTestOutcome"
                }
              }
            }
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `providers:write` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "providers:write"
      }
    },
    "/api/v1/notification-channels": {
      "get": {
        "tags": [
          "Notifications"
        ],
        "summary": "List notification channels",
        "description": "With each channel's last delivery: lastSentAt, and lastError (the last failure after its retries, cleared by the next delivery).\n\nRequires the `providers:read` permission.",
        "operationId": "ListNotificationChannels",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "description": "How many items a page holds, 1 to 200 (default 50).",
            "schema": {
              "maximum": 200,
              "minimum": 1,
              "type": "integer",
              "default": 50
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "The nextCursor of the previous page, to continue after it. An opaque string only the API that issued it understands.",
            "schema": {
              "maxLength": 96,
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PageOfNotificationChannelSummary"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `providers:read` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "providers:read"
      },
      "post": {
        "tags": [
          "Notifications"
        ],
        "summary": "Add a notification channel",
        "description": "Kinds: ntfy, gotify, discord, webhook, email. The secret (a webhook URL, a token or an SMTP password) goes into the vault and is never returned. events lists what the channel is sent: upstream.down, upstream.up, destination.down, destination.up, certificate.expiring, acme.renewal_failed, acme.renewal_recovered, signin.lockout, signin.new_address.\n\nRequires the `providers:write` permission.",
        "operationId": "CreateNotificationChannel",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/NotificationChannelBody"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NotificationChannelSummary"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "415": {
            "description": "The body is not in a type this operation takes.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "422": {
            "description": "The request is well-formed but can't be applied; errors lists why, with a pointer into the body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `providers:write` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "providers:write"
      }
    },
    "/api/v1/notification-channels/{id}": {
      "get": {
        "tags": [
          "Notifications"
        ],
        "summary": "Get a notification channel",
        "description": "Requires the `providers:read` permission.",
        "operationId": "GetNotificationChannel",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "examples": [
                "ntf_0A1b2C3d4E5f6G7h8I9j0K"
              ],
              "pattern": "^ntf_[0-9A-Za-z]{22}$",
              "type": "string",
              "description": "A notification channel: one place events are sent to (an ntfy topic, a Discord webhook, a mailbox). Its secrets are vault SecretIds."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NotificationChannelSummary"
                }
              }
            }
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `providers:read` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "providers:read"
      },
      "put": {
        "tags": [
          "Notifications"
        ],
        "summary": "Replace a notification channel's settings",
        "description": "The kind is fixed. A secret left out keeps the stored one; an empty one removes an optional secret (an ntfy token, an SMTP password without a login, a webhook's signing key).\n\nRequires the `providers:write` permission.",
        "operationId": "ReplaceNotificationChannel",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "examples": [
                "ntf_0A1b2C3d4E5f6G7h8I9j0K"
              ],
              "pattern": "^ntf_[0-9A-Za-z]{22}$",
              "type": "string",
              "description": "A notification channel: one place events are sent to (an ntfy topic, a Discord webhook, a mailbox). Its secrets are vault SecretIds."
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/NotificationChannelBody"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NotificationChannelSummary"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "415": {
            "description": "The body is not in a type this operation takes.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "422": {
            "description": "The request is well-formed but can't be applied; errors lists why, with a pointer into the body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `providers:write` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "providers:write"
      },
      "delete": {
        "tags": [
          "Notifications"
        ],
        "summary": "Delete a notification channel and its secrets",
        "description": "Requires the `providers:write` permission.",
        "operationId": "DeleteNotificationChannel",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "examples": [
                "ntf_0A1b2C3d4E5f6G7h8I9j0K"
              ],
              "pattern": "^ntf_[0-9A-Za-z]{22}$",
              "type": "string",
              "description": "A notification channel: one place events are sent to (an ntfy topic, a Discord webhook, a mailbox). Its secrets are vault SecretIds."
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `providers:write` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "providers:write"
      }
    },
    "/api/v1/notification-channels/{id}/test": {
      "post": {
        "tags": [
          "Notifications"
        ],
        "summary": "Send a test message through a channel",
        "description": "Sends one message now, without retries. A message that could not be delivered is delivered=false with the reason, not an error. The test is audited (notification.channel.test) with its verdict only.\n\nRequires the `providers:write` permission.",
        "operationId": "TestNotificationChannel",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "examples": [
                "ntf_0A1b2C3d4E5f6G7h8I9j0K"
              ],
              "pattern": "^ntf_[0-9A-Za-z]{22}$",
              "type": "string",
              "description": "A notification channel: one place events are sent to (an ntfy topic, a Discord webhook, a mailbox). Its secrets are vault SecretIds."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NotificationTestOutcome"
                }
              }
            }
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `providers:write` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "providers:write"
      }
    },
    "/api/v1/notifications/history": {
      "get": {
        "tags": [
          "Notifications"
        ],
        "summary": "Query the notification history",
        "description": "Every event that was published, newest first, each with what happened on every channel it was addressed to (state, attempts, the next try of a pending one, the last error, when it was sent). An event no channel subscribes to is listed with no deliveries. The filters are all optional and all must match; from (inclusive) and to (exclusive) are the times of publication. History is kept for Paduraksa:Notifications:HistoryRetention.\n\nRequires the `providers:read` permission.",
        "operationId": "QueryNotificationHistory",
        "parameters": [
          {
            "name": "channel",
            "in": "query",
            "description": "Only events with a delivery to this channel (ntf_...).",
            "schema": {
              "examples": [
                "ntf_0A1b2C3d4E5f6G7h8I9j0K"
              ],
              "pattern": "^ntf_[0-9A-Za-z]{22}$",
              "type": "string",
              "description": "A notification channel: one place events are sent to (an ntfy topic, a Discord webhook, a mailbox). Its secrets are vault SecretIds."
            }
          },
          {
            "name": "kind",
            "in": "query",
            "description": "Only this kind of event.",
            "schema": {
              "enum": [
                "upstream.down",
                "upstream.up",
                "destination.down",
                "destination.up",
                "certificate.expiring",
                "acme.renewal_failed",
                "acme.renewal_recovered",
                "signin.lockout",
                "signin.new_address",
                "test"
              ],
              "type": "string"
            }
          },
          {
            "name": "outcome",
            "in": "query",
            "description": "Only events with a delivery in this state (to the channel, when one is given).",
            "schema": {
              "enum": [
                "pending",
                "sent",
                "failed",
                "expired",
                "dropped"
              ],
              "type": "string"
            }
          },
          {
            "name": "from",
            "in": "query",
            "description": "Only events at or after this time (inclusive), ISO 8601 with a zone, e.g. 2026-11-08T12:00:00Z (write the + of an offset as %2B).",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "to",
            "in": "query",
            "description": "Only events before this time (exclusive), ISO 8601 with a zone.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "How many items a page holds, 1 to 200 (default 50).",
            "schema": {
              "maximum": 200,
              "minimum": 1,
              "type": "integer",
              "default": 50
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "The nextCursor of the previous page, to continue after it. An opaque string only the API that issued it understands.",
            "schema": {
              "maxLength": 96,
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PageOfNotificationHistoryEvent"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `providers:read` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "providers:read"
      }
    },
    "/api/v1/notifications/deliveries/{id}/retry": {
      "post": {
        "tags": [
          "Notifications"
        ],
        "summary": "Send a failed or expired delivery again",
        "description": "Puts the delivery back to pending, due now, with a fresh retry schedule; the channel sends it within seconds. Only a failed or expired delivery can be sent again, and only while its channel exists and is not paused (422 otherwise). Audited (notification.delivery.retry) with the delivery, its event and its channel. Delivery is at-least-once, so a receiver that already got the message once may get it again with the same event ID.\n\nRequires the `providers:write` permission.",
        "operationId": "RetryNotificationDelivery",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "examples": [
                "ndl_0A1b2C3d4E5"
              ],
              "pattern": "^ndl_[0-9A-Za-z]{11}$",
              "type": "string",
              "description": "One event's delivery to one channel. Int64Ordered, like its event."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NotificationRetryOutcome"
                }
              }
            }
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "422": {
            "description": "The request is well-formed but can't be applied; errors lists why, with a pointer into the body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `providers:write` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "providers:write"
      }
    },
    "/api/v1/notifications/events/{id}/retry": {
      "post": {
        "tags": [
          "Notifications"
        ],
        "summary": "Send every failed or expired delivery of an event again",
        "description": "As the delivery's retry, for every failed or expired delivery of the event whose channel exists and is not paused. 422 when there is none. Audited (notification.event.retry) with how many.\n\nRequires the `providers:write` permission.",
        "operationId": "RetryNotificationEvent",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "examples": [
                "nev_0A1b2C3d4E5"
              ],
              "pattern": "^nev_[0-9A-Za-z]{11}$",
              "type": "string",
              "description": "A notification event as the outbox stores it: what happened, once, however many channels it was addressed to. Int64Ordered: admin-only,\n            append-only and high-volume."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NotificationRetryOutcome"
                }
              }
            }
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "422": {
            "description": "The request is well-formed but can't be applied; errors lists why, with a pointer into the body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `providers:write` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "providers:write"
      }
    },
    "/api/v1/upstreams/health": {
      "get": {
        "tags": [
          "Upstreams"
        ],
        "summary": "Read every upstream's live health",
        "description": "As the running proxy sees it: each destination's verdict and why, the whole upstream's state (unchecked when it has no health check, unknown, healthy, degraded, down) and its latest change since the process started. Read once a second by a watcher, so it is at most a few seconds old.\n\nRequires the `config:read` permission.",
        "operationId": "ListUpstreamHealth",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/UpstreamHealthView"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `config:read` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "config:read"
      }
    },
    "/api/v1/upstreams/{id}/health": {
      "get": {
        "tags": [
          "Upstreams"
        ],
        "summary": "Read one upstream's live health",
        "description": "Requires the `config:read` permission.",
        "operationId": "GetUpstreamHealth",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "examples": [
                "ups_0A1b2C3d4E5f6G7h8I9j0K"
              ],
              "pattern": "^ups_[0-9A-Za-z]{22}$",
              "type": "string",
              "description": "An upstream, compiled into a YARP cluster."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UpstreamHealthView"
                }
              }
            }
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `config:read` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "config:read"
      }
    },
    "/api/v1/audit": {
      "get": {
        "tags": [
          "Audit"
        ],
        "summary": "Query the audit log",
        "description": "Newest first. The filters are all optional and all must match.\n\nRequires the `audit:read` permission.",
        "operationId": "QueryAudit",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "description": "Only events at or after this time (inclusive), ISO 8601 with a zone, e.g. 2026-11-08T12:00:00Z (write the + of an offset as %2B).",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "to",
            "in": "query",
            "description": "Only events before this time (exclusive), ISO 8601 with a zone.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "actor",
            "in": "query",
            "description": "Only events by this actor: 'system', 'cli', a user ID or an API token ID.",
            "schema": {
              "maxLength": 128,
              "type": "string"
            }
          },
          {
            "name": "action",
            "in": "query",
            "description": "Only this action (config.host.update), or every action under a prefix with one trailing '*' (config.*).",
            "schema": {
              "maxLength": 128,
              "type": "string"
            }
          },
          {
            "name": "target",
            "in": "query",
            "description": "Only events about this object: a typed ID such as host_...",
            "schema": {
              "maxLength": 128,
              "type": "string"
            }
          },
          {
            "name": "outcome",
            "in": "query",
            "description": "Only events with this outcome.",
            "schema": {
              "enum": [
                "success",
                "denied",
                "invalid",
                "conflict",
                "failed"
              ],
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "How many items a page holds, 1 to 200 (default 50).",
            "schema": {
              "maximum": 200,
              "minimum": 1,
              "type": "integer",
              "default": 50
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "The nextCursor of the previous page, to continue after it. An opaque string only the API that issued it understands.",
            "schema": {
              "maxLength": 96,
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PageOfAuditEventSummary"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `audit:read` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "audit:read"
      }
    },
    "/api/v1/audit/verify": {
      "get": {
        "tags": [
          "Audit"
        ],
        "summary": "Check the audit log's hash chain",
        "description": "Walks the whole chain and reports the first event that doesn't verify. Keep headHash somewhere else to detect the removal of the newest events.\n\nRequires the `audit:read` permission.",
        "operationId": "VerifyAudit",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuditVerification"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `audit:read` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "audit:read"
      }
    },
    "/api/v1/logs": {
      "get": {
        "tags": [
          "Logs"
        ],
        "summary": "List the log files",
        "description": "Every log file in the logs directory: the live access.log and error.log and their rotated, gzip-compressed predecessors. Newest first within each log.\n\nRequires the `logs:read` permission.",
        "operationId": "ListLogFiles",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/LogFileSummary"
                  }
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `logs:read` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "logs:read"
      }
    },
    "/api/v1/logs/tail": {
      "get": {
        "tags": [
          "Logs"
        ],
        "summary": "Stream the access log live",
        "description": "Server-Sent Events (text/event-stream) of the requests as they end, newest last: each default event is one JSON entry (id: its sequence number), the entries as access.log would show them (same caps, control characters escaped, sensitive query parameters redacted); a host with its access log off never appears. The filters pick which entries are sent; backlog first sends the newest matching entries that the process still holds. A reader that falls behind loses its oldest entries and is told with an event: skipped whose data is {\"count\":N}. A comment line (: heartbeat) every 15 seconds keeps the stream open. The stream ends when the client disconnects. Each stream holds a buffer of 2048 entries and the process serves 32 at once; the 33rd is refused with 503.\n\nRequires the `logs:read` permission.",
        "operationId": "StreamAccessLog",
        "parameters": [
          {
            "name": "host",
            "in": "query",
            "description": "Only this configured host, by name.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "unmatched",
            "in": "query",
            "description": "true for only the requests no host owned.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "statusClass",
            "in": "query",
            "description": "Only responses of this class: 1 to 5, or 1xx to 5xx.",
            "schema": {
              "enum": [
                "1",
                "2",
                "3",
                "4",
                "5",
                "1xx",
                "2xx",
                "3xx",
                "4xx",
                "5xx"
              ],
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "description": "Only this response status.",
            "schema": {
              "maximum": 599,
              "minimum": 100,
              "type": "integer"
            }
          },
          {
            "name": "ip",
            "in": "query",
            "description": "Only this client address, or the addresses that start with it.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "method",
            "in": "query",
            "description": "Only this request method.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "path",
            "in": "query",
            "description": "Only requests whose path contains this text.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "backlog",
            "in": "query",
            "description": "How many of the newest matching entries the process still holds to send first.",
            "schema": {
              "maximum": 1000,
              "minimum": 0,
              "type": "integer",
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/event-stream": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "503": {
            "description": "The server can't do this right now; try again shortly.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `logs:read` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "logs:read"
      }
    },
    "/api/v1/logs/recent": {
      "get": {
        "tags": [
          "Logs"
        ],
        "summary": "Read the newest access-log entries",
        "description": "The newest requests the process still holds in memory (what the live tail replays as its backlog), oldest first, as one JSON array: the same entries and filters as the live tail, without a stream. A bounded snapshot, so it takes a limit of its own and no cursor. A restart empties it; the log files are the record.\n\nRequires the `logs:read` permission.",
        "operationId": "ListRecentAccessLog",
        "parameters": [
          {
            "name": "host",
            "in": "query",
            "description": "Only this configured host, by name.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "unmatched",
            "in": "query",
            "description": "true for only the requests no host owned.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "statusClass",
            "in": "query",
            "description": "Only responses of this class: 1 to 5, or 1xx to 5xx.",
            "schema": {
              "enum": [
                "1",
                "2",
                "3",
                "4",
                "5",
                "1xx",
                "2xx",
                "3xx",
                "4xx",
                "5xx"
              ],
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "description": "Only this response status.",
            "schema": {
              "maximum": 599,
              "minimum": 100,
              "type": "integer"
            }
          },
          {
            "name": "ip",
            "in": "query",
            "description": "Only this client address, or the addresses that start with it.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "method",
            "in": "query",
            "description": "Only this request method.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "path",
            "in": "query",
            "description": "Only requests whose path contains this text.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "How many entries to return, the newest that match.",
            "schema": {
              "maximum": 1000,
              "minimum": 1,
              "type": "integer",
              "default": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/LogTailEntry"
                  }
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `logs:read` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "logs:read"
      }
    },
    "/api/v1/logs/{name}/tail": {
      "get": {
        "tags": [
          "Logs"
        ],
        "summary": "Read the last lines of a log file",
        "description": "At most 1000 lines (200 by default) out of the last 1 MiB of a plain file, or of the first 8 Mi characters a compressed one is decompressed from, each line cut at 4096 characters, oldest first. A live file and a .gz are both readable.\n\nRequires the `logs:read` permission.",
        "operationId": "TailLogFile",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "lines",
            "in": "query",
            "description": "How many lines to return.",
            "schema": {
              "maximum": 1000,
              "minimum": 1,
              "type": "integer",
              "default": 200
            }
          },
          {
            "name": "filter",
            "in": "query",
            "description": "Only the lines that contain this text (case-insensitive).",
            "schema": {
              "maxLength": 128,
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LogFileTail"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `logs:read` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "logs:read"
      }
    },
    "/api/v1/logs/{name}": {
      "get": {
        "tags": [
          "Logs"
        ],
        "summary": "Download a log file",
        "description": "The file itself, as an attachment (text/plain, or application/gzip for a compressed one), streamed. The access log holds client IPs: treat it accordingly.\n\nRequires the `logs:read` permission.",
        "operationId": "DownloadLogFile",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              },
              "application/gzip": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `logs:read` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "logs:read"
      }
    },
    "/api/v1/security/overview": {
      "get": {
        "tags": [
          "Security"
        ],
        "summary": "Read the security overview",
        "description": "The gate's counters over the range (proof of work, rate limits, access lists, the connection stage, sign-in), what it refused by reason and over time, and each enabled host's protections as the live config compiled them, with what it refused over the range. The counters are kept in the metrics' rollups, so a restart doesn't reset them; from is the first moment of the range the server observed. A step of the series with coveredSeconds 0 is a time the server was not running, not a step with no refusals.\n\nRequires the `config:read` permission.",
        "operationId": "GetSecurityOverview",
        "parameters": [
          {
            "name": "range",
            "in": "query",
            "description": "How far back to count: hour is from memory and the last hour of rollups, the rest from the one-minute rollups.",
            "schema": {
              "enum": [
                "hour",
                "day",
                "week",
                "month"
              ],
              "type": "string",
              "default": "hour"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SecurityOverview"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `config:read` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "config:read"
      }
    },
    "/api/v1/metrics": {
      "get": {
        "tags": [
          "Metrics"
        ],
        "summary": "Scrape the metrics (Prometheus text format)",
        "description": "Request counters and latency histograms by host, route, upstream and destination, and the gate's process totals, in the Prometheus text exposition format 0.0.4. Also served at /metrics. Counters only grow, and a host keeps its series across config changes: its labels are its name and its typed ID.\n\nRequires the `metrics:read` permission.",
        "operationId": "ScrapeMetrics",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain; version=0.0.4; charset=utf-8": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller may not do this.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "metrics:read"
      }
    },
    "/api/v1/metrics/overview": {
      "get": {
        "tags": [
          "Metrics"
        ],
        "summary": "Read the metrics overview",
        "description": "What the dashboard's overview draws: the pace over the last half minute, a chart, the busiest hosts and the upstreams' errors over the range. The chart has every step from the first the server observed to the last; a step with coveredSeconds 0 is a time the server was not running, a gap, and not a zero.\n\nRequires the `metrics:read` permission.",
        "operationId": "GetMetricsOverview",
        "parameters": [
          {
            "name": "range",
            "in": "query",
            "description": "How far back to look: hour is from memory and the last hour of rollups, the rest from the one-minute rollups.",
            "schema": {
              "enum": [
                "hour",
                "day",
                "week",
                "month"
              ],
              "type": "string",
              "default": "hour"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MetricsOverview"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `metrics:read` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "metrics:read"
      }
    },
    "/api/v1/analytics": {
      "get": {
        "tags": [
          "Analytics"
        ],
        "summary": "Read the traffic analytics",
        "description": "Visitors and content, and clients and bots, over a range, for every host or for one. Unique visitors, the top paths, referers and user agents, the slowest paths and the largest responses are approximate (sketches with a stated bound: each row carries its own error); the counts, the status classes and the distributions of agents are exact up to their table sizes. Computed from the access-log drain, so a host with its access log off is absent. The chart has every step from the first the server observed to the last; a step with coveredSeconds 0 is a time the server was not running, a gap, and not a zero.\n\nRequires the `metrics:read` permission.",
        "operationId": "GetAnalytics",
        "parameters": [
          {
            "name": "range",
            "in": "query",
            "description": "How far back to look.",
            "schema": {
              "enum": [
                "hour",
                "day",
                "week",
                "month"
              ],
              "type": "string",
              "default": "hour"
            }
          },
          {
            "name": "host",
            "in": "query",
            "description": "Only this host: its ID (host_..., which may have been deleted since), or 'unmatched' for the requests no host owns. Every host when absent.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AnalyticsOverview"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: a parameter, header or body is unusable (the problem says which).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "401": {
            "description": "Authentication is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "403": {
            "description": "The caller lacks the `metrics:read` permission.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-permission": "metrics:read"
      }
    },
    "/api/v1/ca/root.pem": {
      "get": {
        "tags": [
          "CA"
        ],
        "summary": "Download the local CA root as PEM",
        "description": "Anonymous. The public root certificate only, so the owner can trust it once and the admin UI is then trusted by their browsers.\n\nAnonymous: it needs no credentials.",
        "operationId": "GetCaRootPem",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/x-pem-file": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [ ]
      },
      "head": {
        "tags": [
          "CA"
        ],
        "summary": "Download the local CA root as PEM",
        "description": "Anonymous. The public root certificate only, so the owner can trust it once and the admin UI is then trusted by their browsers.\n\nAnonymous: it needs no credentials.",
        "operationId": "GetCaRootPem",
        "responses": {
          "200": {
            "description": "OK"
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [ ]
      }
    },
    "/api/v1/ca/root.crt": {
      "get": {
        "tags": [
          "CA"
        ],
        "summary": "Download the local CA root as DER",
        "description": "Anonymous. application/x-x509-ca-cert, the type Android and iOS offer to install. The public root certificate only.\n\nAnonymous: it needs no credentials.",
        "operationId": "GetCaRootCrt",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/x-x509-ca-cert": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [ ]
      },
      "head": {
        "tags": [
          "CA"
        ],
        "summary": "Download the local CA root as DER",
        "description": "Anonymous. application/x-x509-ca-cert, the type Android and iOS offer to install. The public root certificate only.\n\nAnonymous: it needs no credentials.",
        "operationId": "GetCaRootCrt",
        "responses": {
          "200": {
            "description": "OK"
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [ ]
      }
    },
    "/api/v1/ca": {
      "get": {
        "tags": [
          "CA"
        ],
        "summary": "Describe the local CA root",
        "description": "Anonymous. The public root's SHA-256 fingerprint, subject and validity: what the wizard shows next to the download, so the owner can check what they are asked to trust.\n\nAnonymous: it needs no credentials.",
        "operationId": "GetCaRoot",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CaRootCertificate"
                }
              }
            }
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [ ]
      },
      "head": {
        "tags": [
          "CA"
        ],
        "summary": "Describe the local CA root",
        "description": "Anonymous. The public root's SHA-256 fingerprint, subject and validity: what the wizard shows next to the download, so the owner can check what they are asked to trust.\n\nAnonymous: it needs no credentials.",
        "operationId": "GetCaRoot",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CaRootCertificate"
                }
              }
            }
          },
          "404": {
            "description": "There is no such object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error; the trace ID in the problem finds it in the server log.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiProblem"
                }
              }
            }
          }
        },
        "security": [ ]
      }
    }
  },
  "components": {
    "schemas": {
      "AccessListBasicAuth": {
        "type": "object",
        "properties": {
          "realm": {
            "type": [
              "null",
              "string"
            ],
            "description": "The realm of the password prompt: 1-64 printable ASCII characters; null = the list's name."
          },
          "users": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/AccessListUserConfig"
            }
          }
        },
        "additionalProperties": false,
        "description": "An access list's basic-auth users."
      },
      "AccessListConfig": {
        "required": [
          "id",
          "name"
        ],
        "type": "object",
        "properties": {
          "id": {
            "description": "Identity.",
            "$ref": "#/components/schemas/AccessListId"
          },
          "name": {
            "type": "string",
            "description": "Human label, unique ignoring case. The default `realm` of the password prompt."
          },
          "version": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Concurrency version.",
            "format": "int64",
            "default": 0
          },
          "notes": {
            "type": [
              "null",
              "string"
            ],
            "description": "Free text."
          },
          "ip": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "The address rules; null = none.",
                "$ref": "#/components/schemas/AccessListIp"
              }
            ]
          },
          "basicAuth": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "The users and their realm; null = no password.",
                "$ref": "#/components/schemas/AccessListBasicAuth"
              }
            ]
          },
          "satisfy": {
            "enum": [
              "all",
              "any"
            ],
            "type": "string",
            "description": "AccessListSatisfy.All (built-in): the address must pass and then, when the list has\n        users, the credentials. AccessListSatisfy.Any: either passes, so an address in the allow list needs no\n        password. A denied address is refused either way.",
            "default": "all"
          },
          "passCredentialsUpstream": {
            "type": "boolean",
            "description": "Forward the `Authorization` header to the upstream. Off: the proxy's\n            credentials are removed once the list has accepted them, because the upstream has no use for them.",
            "default": false
          }
        },
        "additionalProperties": false,
        "description": "A named set of rules that hosts and routes apply to who may reach them: IP allow and deny entries, and HTTP basic-auth\nusers, combined as AccessListSatisfy AccessListConfig.Satisfy says. It is a top-level object, so one list can guard several hosts and\nroutes, and it replaces the inline IP allow list the settings used to carry."
      },
      "AccessListCounters": {
        "required": [
          "addressRefused",
          "unauthorized",
          "cacheHits",
          "verifications",
          "queued",
          "queueFull",
          "queueTimeouts"
        ],
        "type": "object",
        "properties": {
          "addressRefused": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Requests refused for their address (403, or dropped).",
            "format": "int64"
          },
          "unauthorized": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Requests answered 401: no credentials, or wrong ones.",
            "format": "int64"
          },
          "cacheHits": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Passwords accepted from the five-minute memory, without a check.",
            "format": "int64"
          },
          "verifications": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Password checks run.",
            "format": "int64"
          },
          "queued": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Requests that waited for a password check to come free.",
            "format": "int64"
          },
          "queueFull": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Requests refused with 429 at once: the line was full, or their client already had its share of it.",
            "format": "int64"
          },
          "queueTimeouts": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Requests refused with 429 after waiting the maximum.",
            "format": "int64"
          }
        },
        "additionalProperties": false,
        "description": "The access lists' counters."
      },
      "AccessListCountries": {
        "type": "object",
        "properties": {
          "allow": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "type": "string"
            }
          },
          "deny": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "type": "string"
            }
          }
        },
        "additionalProperties": false,
        "description": "The country rules of an access list. The country is the one Cloudflare reports in `CF-IPCountry`, so they only\n            apply in a Cloudflare trusted-proxy mode; elsewhere every request's country is unknown (the config warns:\n            `accessList.countries.noSource`). Codes are ISO 3166-1 alpha-2 (`DE`, upper case in a stored document) plus\n            `T1` (Tor exit nodes) and `XX` (country unknown)."
      },
      "AccessListId": {
        "examples": [
          "acl_0A1b2C3d4E5f6G7h8I9j0K"
        ],
        "pattern": "^acl_[0-9A-Za-z]{22}$",
        "type": "string",
        "description": "An access list: IP allow and deny entries and basic-auth users, which hosts and routes name."
      },
      "AccessListIp": {
        "type": "object",
        "properties": {
          "dropDenied": {
            "type": "boolean",
            "description": "Abort a refused connection instead of answering 403.",
            "default": false
          },
          "countries": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "The country rules; null = none.",
                "$ref": "#/components/schemas/AccessListCountries"
              }
            ]
          },
          "allow": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "type": "string"
            }
          },
          "deny": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "type": "string"
            }
          }
        },
        "additionalProperties": false,
        "description": "The address rules of an access list. Deny wins over allow; an empty allow list means any address that isn't\n            denied, and a non-empty one means only those. The countries are part of the same rules: a request must pass every allow list\n            that has entries and match no deny list."
      },
      "AccessListOverride": {
        "required": [
          "routes"
        ],
        "type": "object",
        "properties": {
          "routes": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "How many routes.",
            "format": "int32"
          },
          "list": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "The list those routes apply; absent when they are not guarded.",
                "$ref": "#/components/schemas/AccessListRef"
              }
            ]
          }
        },
        "additionalProperties": false,
        "description": "Routes guarded by another access list, or none."
      },
      "AccessListProtection": {
        "required": [
          "overrides"
        ],
        "type": "object",
        "properties": {
          "overrides": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AccessListOverride"
            },
            "description": "The routes that differ from the host's list, grouped by what guards them instead."
          },
          "list": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "The list the host names; absent when it names none.",
                "$ref": "#/components/schemas/AccessListRef"
              }
            ]
          }
        },
        "additionalProperties": false,
        "description": "A host's access-list setting."
      },
      "AccessListRef": {
        "required": [
          "id",
          "name",
          "users",
          "addressRanges"
        ],
        "type": "object",
        "properties": {
          "id": {
            "description": "The list.",
            "$ref": "#/components/schemas/AccessListId"
          },
          "name": {
            "type": "string",
            "description": "Its name."
          },
          "users": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "How many users it asks a password of; 0 means addresses only.",
            "format": "int32"
          },
          "addressRanges": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "The address ranges its allow and deny entries cover, after merging overlaps.",
            "format": "int32"
          },
          "countries": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "The countries (and Tor or unknown) its allow and deny lists name.",
            "format": "int32",
            "default": 0
          }
        },
        "additionalProperties": false,
        "description": "A named access list, with what it asks of a visitor."
      },
      "AccessListSettings": {
        "type": "object",
        "properties": {
          "enabled": {
            "type": [
              "null",
              "boolean"
            ],
            "description": "Apply the resolved list. Built-in default: on. Off lifts an inherited list."
          },
          "accessListId": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "The list to apply; null = inherit. With bool? AccessListSettings.Enabled off the reference is kept but inert.",
                "$ref": "#/components/schemas/AccessListId"
              }
            ]
          }
        },
        "additionalProperties": false,
        "description": "Which access list a layer applies. AccessListId? AccessListSettings.AccessListId names a list the document declares; both fields\n            inherit on their own, so a host can turn an inherited list off without forgetting which one it was, and a route can\n            inherit its host's, name another, or turn it off. A layer that names a list without stating\n            bool? AccessListSettings.Enabled leaves the switch to the built-in default, which is on."
      },
      "AccessListUserConfig": {
        "required": [
          "username"
        ],
        "type": "object",
        "properties": {
          "username": {
            "type": "string",
            "description": "1-64 visible ASCII characters, no colon, case-sensitive."
          },
          "credentialId": {
            "examples": [
              "pwc_0A1b2C3d4E5f6G7h8I9j0K"
            ],
            "pattern": "^pwc_[0-9A-Za-z]{22}$",
            "type": [
              "null",
              "string"
            ],
            "description": "The vault's handle on this user's password. The server makes it when a user is first written with a\n            Password and keeps it when the password changes, so a rollback restores the user but not an older password.\n            A user whose credential the vault doesn't hold (deleted since, or never set) can't sign in, and the config says so in a warning\n            (`accessList.user.noPassword`). A write that names a user already on the list and sends neither a password nor an ID keeps\n            the credential the list has."
          },
          "password": {
            "type": [
              "null",
              "string"
            ],
            "description": "Write-only: the plain password of a new or changed user, 1-256 bytes in UTF-8. The server hands it to the\n            vault before anything is validated, stored or audited and never returns it, so it is null in every document it hands out."
          }
        },
        "additionalProperties": false,
        "description": "One basic-auth user. The document names the user's password in the vault and never holds it, or anything that\n            verifies it: no read returns a password or a hash."
      },
      "AcmeAccountId": {
        "examples": [
          "acct_0A1b2C3d4E5f6G7h8I9j0K"
        ],
        "pattern": "^acct_[0-9A-Za-z]{22}$",
        "type": "string",
        "description": "An ACME account. Its account key lives in the vault under this ID."
      },
      "AcmeAccountStatus": {
        "enum": [
          "active",
          "deactivated"
        ],
        "type": "string",
        "description": "The state of an ACME account row. Persisted by name (ModelNames)."
      },
      "AcmeAccountSummary": {
        "required": [
          "id",
          "directoryUrl",
          "contacts",
          "status",
          "accountUrl",
          "keyAlgorithm",
          "termsOfServiceAgreed",
          "createdAt",
          "updatedAt"
        ],
        "type": "object",
        "properties": {
          "id": {
            "$ref": "#/components/schemas/AcmeAccountId"
          },
          "directoryUrl": {
            "type": "string"
          },
          "contacts": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "status": {
            "$ref": "#/components/schemas/AcmeAccountStatus"
          },
          "accountUrl": {
            "type": "string"
          },
          "keyAlgorithm": {
            "$ref": "#/components/schemas/JwsAlgorithm"
          },
          "termsOfServiceAgreed": {
            "type": "boolean"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          },
          "eabKid": {
            "type": [
              "null",
              "string"
            ]
          },
          "label": {
            "type": [
              "null",
              "string"
            ]
          }
        },
        "additionalProperties": false,
        "description": "An ACME account as listed. No key material and no vault reference: the account key and the external-account-binding secret stay in the\nvault, named only by the internal row."
      },
      "AcmeChallengeType": {
        "enum": [
          "http-01",
          "dns-01"
        ],
        "type": "string",
        "description": "The ACME challenge type a certificate's names are proven with. Persisted by name (ModelNames), the same as in RFC 8555."
      },
      "AcmeOrderId": {
        "examples": [
          "ord_0A1b2C3d4E5f6G7h8I9j0K"
        ],
        "pattern": "^ord_[0-9A-Za-z]{22}$",
        "type": "string",
        "description": "An ACME order, i.e. one issuance or renewal attempt for a certificate."
      },
      "AcmeOrderStatus": {
        "enum": [
          "pending",
          "ordered",
          "finalizing",
          "valid",
          "failed"
        ],
        "type": "string",
        "description": "Where an ACME order stands in Paduraksa's own bookkeeping. The CA's order is the authority on what\n            happens next, so a resume re-reads it; this status is for operators and for the scheduler to see what is\n            unfinished."
      },
      "AcmeOrderSummary": {
        "required": [
          "certificateId",
          "orderId",
          "accountId",
          "status",
          "challengeType",
          "keySpec",
          "names",
          "attempts",
          "createdAt",
          "updatedAt"
        ],
        "type": "object",
        "properties": {
          "certificateId": {
            "$ref": "#/components/schemas/CertificateId"
          },
          "orderId": {
            "$ref": "#/components/schemas/AcmeOrderId"
          },
          "accountId": {
            "$ref": "#/components/schemas/AcmeAccountId"
          },
          "status": {
            "$ref": "#/components/schemas/AcmeOrderStatus"
          },
          "challengeType": {
            "$ref": "#/components/schemas/AcmeChallengeType"
          },
          "keySpec": {
            "$ref": "#/components/schemas/KeySpec"
          },
          "names": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "attempts": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "format": "int32"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          },
          "dnsProviderId": {
            "examples": [
              "dnsp_0A1b2C3d4E5f6G7h8I9j0K"
            ],
            "pattern": "^dnsp_[0-9A-Za-z]{22}$",
            "type": [
              "null",
              "string"
            ],
            "description": "A DnsProviderId: 'dnsp_' and 22 base62 characters."
          },
          "profile": {
            "type": [
              "null",
              "string"
            ]
          },
          "preferredChain": {
            "type": [
              "null",
              "string"
            ]
          },
          "error": {
            "type": [
              "null",
              "string"
            ]
          },
          "lastAttemptAt": {
            "type": [
              "null",
              "string"
            ],
            "format": "date-time"
          },
          "retryAt": {
            "type": [
              "null",
              "string"
            ],
            "format": "date-time"
          },
          "nextRenewalAt": {
            "type": [
              "null",
              "string"
            ],
            "format": "date-time"
          },
          "ariWindowStart": {
            "type": [
              "null",
              "string"
            ],
            "format": "date-time"
          },
          "ariWindowEnd": {
            "type": [
              "null",
              "string"
            ],
            "format": "date-time"
          },
          "ariExplanationUrl": {
            "type": [
              "null",
              "string"
            ]
          },
          "ariCheckAt": {
            "type": [
              "null",
              "string"
            ],
            "format": "date-time"
          }
        },
        "additionalProperties": false,
        "description": "The ACME state of one certificate: what the last attempt did and when the scheduler acts next."
      },
      "ActiveHealthConfig": {
        "type": "object",
        "properties": {
          "path": {
            "type": "string",
            "description": "Starts with `/`, no query or fragment; appended to each destination's address.",
            "default": "/"
          },
          "interval": {
            "pattern": "^-?(\\d+\\.)?\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,7})?$",
            "type": [
              "null",
              "string"
            ],
            "description": "Positive; null = TimeSpan ActiveHealthConfig.DefaultInterval (canonical form: set)."
          },
          "timeout": {
            "pattern": "^-?(\\d+\\.)?\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,7})?$",
            "type": [
              "null",
              "string"
            ],
            "description": "Positive; null = TimeSpan ActiveHealthConfig.DefaultTimeout (canonical form: set)."
          }
        },
        "additionalProperties": false,
        "description": "Active health: a GET of Path on each destination every Interval."
      },
      "ActorKind": {
        "enum": [
          "system",
          "cli",
          "user",
          "token"
        ],
        "type": "string",
        "description": "What kind of principal acted. Persisted by name (ModelNames)."
      },
      "ActorRef": {
        "required": [
          "kind"
        ],
        "type": "object",
        "properties": {
          "kind": {
            "description": "The kind of actor.",
            "$ref": "#/components/schemas/ActorKind"
          },
          "user": {
            "examples": [
              "usr_0A1b2C3d4E5f6G7h8I9j0K"
            ],
            "pattern": "^usr_[0-9A-Za-z]{22}$",
            "type": [
              "null",
              "string"
            ],
            "description": "The user, for ActorKind.User."
          },
          "token": {
            "examples": [
              "tok_0A1b2C3d4E5f6G7h8I9j0K"
            ],
            "pattern": "^tok_[0-9A-Za-z]{22}$",
            "type": [
              "null",
              "string"
            ],
            "description": "The API token, for ActorKind.Token."
          }
        },
        "additionalProperties": false,
        "description": "Who did something, as the history records it."
      },
      "AddDraftEntriesRequest": {
        "required": [
          "commands"
        ],
        "type": "object",
        "properties": {
          "commands": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ConfigCommand"
            },
            "description": "Upsert and delete commands (never a replace or rollback). Their `dryRun`, `force` and `message` are ignored."
          }
        },
        "additionalProperties": false,
        "description": "The body of \"add entries\": object commands, added in order. Each replaces an earlier entry for the same object."
      },
      "AnalyticsBot": {
        "required": [
          "kind",
          "name",
          "count"
        ],
        "type": "object",
        "properties": {
          "kind": {
            "type": "string",
            "description": "The kind: `searchEngine`, `aiCrawler`, `monitor`, `socialPreview`, `scanner`, `library` or `unknown`."
          },
          "name": {
            "type": "string",
            "description": "The bot's name (`Googlebot`, `curl`), or empty for a row that counts a whole kind."
          },
          "count": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Requests.",
            "format": "int64"
          }
        },
        "additionalProperties": false,
        "description": "A kind or name of bot with how many requests it made."
      },
      "AnalyticsCount": {
        "required": [
          "label",
          "count",
          "error"
        ],
        "type": "object",
        "properties": {
          "label": {
            "type": "string",
            "description": "What was counted: a path, a referer's scheme and host (or `direct`, `(same site)`), a user agent, a browser, a status code."
          },
          "count": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "How many requests (or, for the largest responses, how many bytes).",
            "format": "int64"
          },
          "error": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "The most Count may overshoot by; 0 when exact.",
            "format": "int64"
          }
        },
        "additionalProperties": false,
        "description": "A label and how many times it was seen, for a table or a distribution. Error is how far Count may be\nabove the truth when the table comes from a sketch (top paths, referers, user agents, largest responses): the true count is at least\n`Count - Error` and at most `Count`. It is 0 for a count that is exact."
      },
      "AnalyticsOverview": {
        "required": [
          "at",
          "range",
          "stepSeconds",
          "totals",
          "series",
          "statusClasses",
          "statuses",
          "methods",
          "protocols",
          "topPaths",
          "topReferers",
          "topUserAgents",
          "slowestPaths",
          "largestResponses",
          "browsers",
          "browserVersions",
          "operatingSystems",
          "devices",
          "botKinds",
          "topBots",
          "botsIgnoringRobots"
        ],
        "type": "object",
        "properties": {
          "at": {
            "type": "string",
            "description": "When it was read.",
            "format": "date-time"
          },
          "range": {
            "description": "How far back the figures reach.",
            "$ref": "#/components/schemas/MetricsRange"
          },
          "stepSeconds": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "The usual width of one Series step: 5 minutes for the hour, 1 hour for the day, 3 hours for the week and 1 day for the month.",
            "format": "int32"
          },
          "totals": {
            "description": "The headline figures.",
            "$ref": "#/components/schemas/AnalyticsTotals"
          },
          "series": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AnalyticsStep"
            },
            "description": "The charts, oldest first, from the first step the server observed to the last: every step between them is present, and one the server was not running for has int AnalyticsStep.CoveredSeconds 0."
          },
          "statusClasses": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AnalyticsCount"
            },
            "description": "Requests by status class: `none` (no response), `1xx` to `5xx`. Exact."
          },
          "statuses": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AnalyticsCount"
            },
            "description": "The most common exact status codes. Exact up to 64 distinct codes a bucket."
          },
          "methods": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AnalyticsCount"
            },
            "description": "Requests by method. Exact up to 16 distinct methods a bucket."
          },
          "protocols": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AnalyticsCount"
            },
            "description": "Requests by HTTP version: `HTTP/1.0`, `HTTP/1.1`, `HTTP/2`, `HTTP/3`, `other`. Exact. (The TLS version is not available to the analytics without work on the request path.)"
          },
          "topPaths": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AnalyticsPath"
            },
            "description": "The most requested paths."
          },
          "topReferers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AnalyticsCount"
            },
            "description": "Where requests came from: `direct`, `(same site)` and referers reduced to scheme and host."
          },
          "topUserAgents": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AnalyticsCount"
            },
            "description": "The most common raw user agents."
          },
          "slowestPaths": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AnalyticsPath"
            },
            "description": "Paths with the highest 95th-percentile time among the tracked ones with at least five requests."
          },
          "largestResponses": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AnalyticsCount"
            },
            "description": "Paths by the bytes of their declared responses (the count is bytes)."
          },
          "browsers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AnalyticsCount"
            },
            "description": "Browser families of the clients that are not bots."
          },
          "browserVersions": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AnalyticsCount"
            },
            "description": "Browser families with their major version, for the clients that are not bots."
          },
          "operatingSystems": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AnalyticsCount"
            },
            "description": "Operating system families of the clients that are not bots."
          },
          "devices": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AnalyticsCount"
            },
            "description": "Requests by device: `desktop`, `mobile`, `tablet`, `bot`, `other`. Exact."
          },
          "botKinds": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AnalyticsBot"
            },
            "description": "Bot requests by kind. Exact."
          },
          "topBots": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AnalyticsBot"
            },
            "description": "The most active bots by name."
          },
          "botsIgnoringRobots": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AnalyticsBot"
            },
            "description": "Bots that fetched a path the host's `robots.txt` disallows for them, by name and kind. Only hosts that serve a managed `robots.txt` count; monitors and generic libraries are not expected to read it and are left out."
          },
          "host": {
            "examples": [
              "host_0A1b2C3d4E5f6G7h8I9j0K"
            ],
            "pattern": "^host_[0-9A-Za-z]{22}$",
            "type": [
              "null",
              "string"
            ],
            "description": "The host the figures are for, or `null` for all of them (and for the requests no host owns, which have no ID: see Unmatched)."
          },
          "unmatched": {
            "type": "boolean",
            "description": "True when the figures are for the requests no host owns (scanners and typos).",
            "default": false
          }
        },
        "additionalProperties": false,
        "description": "The traffic analytics of a range, for all hosts or one: visitors and content, and clients and bots (docs/design/observability.md \"Traffic analytics\"). Computed from the\naccess-log drain, so a host with its access log off is absent. Visitors, the top tables and the per-path figures are approximate by design;\neach table row carries its own bound."
      },
      "AnalyticsPath": {
        "required": [
          "path",
          "requests",
          "error",
          "bytesOut",
          "p95Ms",
          "errorRate"
        ],
        "type": "object",
        "properties": {
          "path": {
            "type": "string",
            "description": "The decoded path without its query string, at most 256 characters."
          },
          "requests": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Requests to it, an upper bound that may overshoot by Error.",
            "format": "int64"
          },
          "error": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "The most Requests may overshoot by.",
            "format": "int64"
          },
          "bytesOut": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Response body bytes since the path was first tracked in a sketch; a lower bound on its total.",
            "format": "int64"
          },
          "p95Ms": {
            "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?$",
            "type": [
              "number",
              "string"
            ],
            "description": "The 95th-percentile total time in milliseconds, over the requests seen since the path was first tracked.",
            "format": "double"
          },
          "errorRate": {
            "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?$",
            "type": [
              "number",
              "string"
            ],
            "description": "The share of those requests answered with a 5xx, 0 to 1.",
            "format": "double"
          }
        },
        "additionalProperties": false,
        "description": "One path in a table of paths."
      },
      "AnalyticsStep": {
        "required": [
          "start",
          "widthSeconds",
          "coveredSeconds",
          "requests",
          "bytesOut",
          "bytesIn",
          "visitors",
          "serverErrors"
        ],
        "type": "object",
        "properties": {
          "start": {
            "type": "string",
            "description": "The step's first second.",
            "format": "date-time"
          },
          "widthSeconds": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "How long the step is.",
            "format": "int32"
          },
          "coveredSeconds": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "How many of those seconds the server was running to count them.",
            "format": "int32"
          },
          "requests": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Requests in the step.",
            "format": "int64"
          },
          "bytesOut": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Response body bytes written to clients in the step.",
            "format": "int64"
          },
          "bytesIn": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Request body bytes read from clients in the step.",
            "format": "int64"
          },
          "visitors": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Estimated unique visitors in the step (HyperLogLog, about 1.6 % standard error).",
            "format": "int64"
          },
          "serverErrors": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "5xx answers in the step.",
            "format": "int64"
          }
        },
        "additionalProperties": false,
        "description": "One step of the charts: what happened over the WidthSeconds from Start. A step whose\nCoveredSeconds is 0 has no data: Paduraksa was not running (every figure is 0), a gap and not a quiet step. A step that was\ncovered and saw no requests has requests 0."
      },
      "AnalyticsTotals": {
        "required": [
          "requests",
          "bytesOut",
          "bytesIn",
          "visitors",
          "serverErrors",
          "errorRate",
          "botRequests",
          "coveredSeconds"
        ],
        "type": "object",
        "properties": {
          "requests": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Requests answered, the connections the gate dropped included.",
            "format": "int64"
          },
          "bytesOut": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Response body bytes written to clients, as they went out (an upstream's compression counts compressed). Stretches stored\n            before bodies were counted hold the declared `Content-Length`s instead.",
            "format": "int64"
          },
          "bytesIn": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Request body bytes read from clients; none for stretches stored before they were counted.",
            "format": "int64"
          },
          "visitors": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Estimated unique visitors over the whole range, the union of the steps' and not their sum (about 1.6 % standard error). A visitor active across a restart counts once on each side of it.",
            "format": "int64"
          },
          "serverErrors": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Requests answered with a 5xx.",
            "format": "int64"
          },
          "errorRate": {
            "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?$",
            "type": [
              "number",
              "string"
            ],
            "description": "ServerErrors over Requests, 0 to 1.",
            "format": "double"
          },
          "botRequests": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Requests whose user agent is an automated client.",
            "format": "int64"
          },
          "coveredSeconds": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "How many seconds of the range the server was running for.",
            "format": "int64"
          }
        },
        "additionalProperties": false,
        "description": "The headline figures of the range."
      },
      "ApiProblem": {
        "required": [
          "type",
          "title",
          "status"
        ],
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "description": "A URI naming the kind of problem (the HTTP status's RFC 9110 section)."
          },
          "title": {
            "type": "string",
            "description": "A short, fixed summary of the kind of problem."
          },
          "status": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "The HTTP status code.",
            "format": "int32"
          },
          "detail": {
            "type": [
              "null",
              "string"
            ],
            "description": "What went wrong this time. Never an exception message or a stack trace."
          },
          "instance": {
            "type": [
              "null",
              "string"
            ],
            "description": "The resource the problem is about, when there is one (the certificate a failed issuance kept)."
          },
          "traceId": {
            "type": [
              "null",
              "string"
            ],
            "description": "(wire name `traceId`) Identifies the request in the server's log; an unexpected failure's details are only there."
          },
          "errors": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/ConfigError"
            },
            "description": "Validation errors: what is wrong with what the caller sent (string ConfigError.Path is a JSON\n            pointer into the request body)."
          },
          "relatedErrors": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/ConfigError"
            },
            "description": "Errors the change would cause elsewhere in the config."
          },
          "warnings": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/ConfigWarning"
            },
            "description": "What compiles but probably isn't intended."
          },
          "conflict": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "For a version mismatch: what is current now.",
                "$ref": "#/components/schemas/ConfigConflict"
              }
            ]
          },
          "code": {
            "type": [
              "null",
              "string"
            ],
            "description": "A stable machine-readable name for the kind of failure (`csrf`, `setup_required`, `publish_failed`), when there is one."
          },
          "revision": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "For `publish_failed`: the revision that was stored but is not live.",
                "$ref": "#/components/schemas/RevisionRef"
              }
            ]
          },
          "version": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "description": "For `publish_failed`: the object's new version (the `ETag` to send with the next change).",
            "format": "int64"
          },
          "review": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "For a draft that was not applied because what it would commit is not what was reviewed: the review as it is now, with its token.",
                "$ref": "#/components/schemas/DraftOutcome"
              }
            ]
          }
        },
        "additionalProperties": false,
        "description": "An error answer of the REST API: an RFC 9457 problem details object (`application/problem+json`). Every error\nthe admin app answers under `/api` has this shape, whoever produced it: `type`, `title`, `status`,\n`detail` and `traceId` are the standard members, the rest are extension members that appear when they apply."
      },
      "ApiTokenId": {
        "examples": [
          "tok_0A1b2C3d4E5f6G7h8I9j0K"
        ],
        "pattern": "^tok_[0-9A-Za-z]{22}$",
        "type": "string",
        "description": "An API token."
      },
      "ApiTokenSummary": {
        "required": [
          "id",
          "userId",
          "name",
          "scopes",
          "expiresAt",
          "createdAt",
          "lastUsedAt",
          "revoked"
        ],
        "type": "object",
        "properties": {
          "id": {
            "description": "The token.",
            "$ref": "#/components/schemas/ApiTokenId"
          },
          "userId": {
            "description": "The user it acts as; it can never do more than they can.",
            "$ref": "#/components/schemas/UserId"
          },
          "name": {
            "type": "string",
            "description": "What the owner called it."
          },
          "scopes": {
            "type": "array",
            "description": "What it may do: a subset of what its owner may do."
          },
          "expiresAt": {
            "type": [
              "null",
              "string"
            ],
            "description": "When it stops working, or null for never.",
            "format": "date-time"
          },
          "createdAt": {
            "type": "string",
            "description": "When it was issued.",
            "format": "date-time"
          },
          "lastUsedAt": {
            "type": [
              "null",
              "string"
            ],
            "description": "When it was last used (to within the touch interval), or null.",
            "format": "date-time"
          },
          "revoked": {
            "type": "boolean",
            "description": "It was revoked."
          }
        },
        "additionalProperties": false,
        "description": "An API token's public facts. The secret is shown once, when it is issued, and never again."
      },
      "AuditBreak": {
        "required": [
          "eventId",
          "problem",
          "description"
        ],
        "type": "object",
        "properties": {
          "eventId": {
            "description": "The event. Every one before it verifies.",
            "$ref": "#/components/schemas/AuditEventId"
          },
          "problem": {
            "type": "string",
            "description": "The kind of break: `previousHashMismatch`, `hashMismatch` or `unreadable`."
          },
          "description": {
            "type": "string",
            "description": "A sentence for an operator."
          }
        },
        "additionalProperties": false,
        "description": "The first event of the chain that doesn't verify."
      },
      "AuditEventId": {
        "examples": [
          "aud_0A1b2C3d4E5"
        ],
        "pattern": "^aud_[0-9A-Za-z]{11}$",
        "type": "string",
        "description": "An audit-log entry. Int64Ordered: admin-only and append-only."
      },
      "AuditEventSummary": {
        "required": [
          "id",
          "timestamp",
          "actor",
          "channel",
          "action",
          "outcome"
        ],
        "type": "object",
        "properties": {
          "id": {
            "description": "The event.",
            "$ref": "#/components/schemas/AuditEventId"
          },
          "timestamp": {
            "type": "string",
            "description": "When it was appended (UTC).",
            "format": "date-time"
          },
          "actor": {
            "description": "Who acted.",
            "$ref": "#/components/schemas/ActorRef"
          },
          "channel": {
            "description": "Where it came in.",
            "$ref": "#/components/schemas/Channel"
          },
          "action": {
            "type": "string",
            "description": "The dotted action name, e.g. `config.host.update`."
          },
          "outcome": {
            "description": "How it ended.",
            "$ref": "#/components/schemas/AuditOutcome"
          },
          "target": {
            "type": [
              "null",
              "string"
            ],
            "description": "The typed-ID string of what was acted on, if any."
          },
          "clientIp": {
            "type": [
              "null",
              "string"
            ],
            "description": "The client address, if the action came in over the network."
          },
          "details": {
            "description": "Extra details the action recorded: a JSON object, never a secret."
          },
          "revisionId": {
            "examples": [
              "rev_0A1b2C3d4E5f6G7h8I9j0K"
            ],
            "pattern": "^rev_[0-9A-Za-z]{22}$",
            "type": [
              "null",
              "string"
            ],
            "description": "The config revision it committed, if any."
          }
        },
        "additionalProperties": false,
        "description": "An audit event, as the log reads back. The hashes of the chain are left to AuditVerification."
      },
      "AuditOutcome": {
        "enum": [
          "success",
          "denied",
          "invalid",
          "conflict",
          "failed"
        ],
        "type": "string",
        "description": "How an audited action ended. Persisted by name (ModelNames)."
      },
      "AuditVerification": {
        "required": [
          "intact",
          "verifiedEvents"
        ],
        "type": "object",
        "properties": {
          "intact": {
            "type": "boolean",
            "description": "Every event verified."
          },
          "verifiedEvents": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "How many events verified, from the first up to the break.",
            "format": "int64"
          },
          "head": {
            "examples": [
              "aud_0A1b2C3d4E5"
            ],
            "pattern": "^aud_[0-9A-Za-z]{11}$",
            "type": [
              "null",
              "string"
            ],
            "description": "The last verified event."
          },
          "headHash": {
            "type": [
              "null",
              "string"
            ],
            "description": "Its hash, lowercase hex: keep a copy elsewhere to detect the removal of the newest events."
          },
          "break": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "The first break, when there is one.",
                "$ref": "#/components/schemas/AuditBreak"
              }
            ]
          }
        },
        "additionalProperties": false,
        "description": "The result of walking the audit hash chain."
      },
      "AuthKind": {
        "enum": [
          "unspecified",
          "session",
          "token"
        ],
        "type": "string",
        "description": "How the caller of an admin request authenticated, as the wire says it: `session`, `token` or `unspecified`."
      },
      "AuthProviderConfig": {
        "required": [
          "id",
          "name"
        ],
        "type": "object",
        "properties": {
          "id": {
            "description": "Identity.",
            "$ref": "#/components/schemas/AuthProviderId"
          },
          "name": {
            "type": "string",
            "description": "Human label, unique ignoring case."
          },
          "version": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Concurrency version.",
            "format": "int64",
            "default": 0
          },
          "notes": {
            "type": [
              "null",
              "string"
            ],
            "description": "Free text."
          },
          "forwardAuth": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "The forward-auth endpoint. Required today; a later kind (native OIDC) is another property, and exactly one is set.",
                "$ref": "#/components/schemas/ForwardAuthConfig"
              }
            ]
          }
        },
        "additionalProperties": false,
        "description": "A named way to ask who a request is: today a forward-auth endpoint, which every request is checked against before it is\nforwarded. It is a top-level object, so one provider can guard several hosts and routes. It holds no secret: the session lives\nin the browser's cookie and the endpoint is the operator's own service."
      },
      "AuthProviderId": {
        "examples": [
          "authp_0A1b2C3d4E5f6G7h8I9j0K"
        ],
        "pattern": "^authp_[0-9A-Za-z]{22}$",
        "type": "string",
        "description": "An authentication provider: a forward-auth endpoint (authentik, Authelia, oauth2-proxy or any other) that hosts and routes\n            name to ask who a request is before it is forwarded."
      },
      "AuthProviderOverride": {
        "required": [
          "routes"
        ],
        "type": "object",
        "properties": {
          "routes": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "How many routes.",
            "format": "int32"
          },
          "provider": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "The provider those routes ask; absent when they ask none.",
                "$ref": "#/components/schemas/AuthProviderRef"
              }
            ]
          }
        },
        "additionalProperties": false,
        "description": "Routes that ask another auth provider, or none."
      },
      "AuthProviderProtection": {
        "required": [
          "overrides"
        ],
        "type": "object",
        "properties": {
          "overrides": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AuthProviderOverride"
            },
            "description": "The routes that differ from the host's provider, grouped by what asks them instead."
          },
          "provider": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "The provider the host names; absent when it names none.",
                "$ref": "#/components/schemas/AuthProviderRef"
              }
            ]
          }
        },
        "additionalProperties": false,
        "description": "A host's auth-provider setting."
      },
      "AuthProviderRef": {
        "required": [
          "id",
          "name",
          "endpoint",
          "allowed",
          "refused",
          "failures"
        ],
        "type": "object",
        "properties": {
          "id": {
            "description": "The provider.",
            "$ref": "#/components/schemas/AuthProviderId"
          },
          "name": {
            "type": "string",
            "description": "Its name."
          },
          "endpoint": {
            "type": "string",
            "description": "Its endpoint."
          },
          "allowed": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Requests it let through over the range, from its answer or its decision cache, across every host that asks it.",
            "format": "int64"
          },
          "refused": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Requests it refused or sent to a sign-in page.",
            "format": "int64"
          },
          "failures": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Calls that failed, timed out or were refused for being too many.",
            "format": "int64"
          }
        },
        "additionalProperties": false,
        "description": "A named auth provider with what it has decided since the process started, across every host and route that asks it."
      },
      "AuthProviderSettings": {
        "type": "object",
        "properties": {
          "enabled": {
            "type": [
              "null",
              "boolean"
            ],
            "description": "Ask the resolved provider. Built-in default: on. Off lifts an inherited provider."
          },
          "authProviderId": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "The provider to ask; null = inherit. With bool? AuthProviderSettings.Enabled off the reference is kept but inert.",
                "$ref": "#/components/schemas/AuthProviderId"
              }
            ]
          }
        },
        "additionalProperties": false,
        "description": "Which auth provider a layer applies. AuthProviderId? AuthProviderSettings.AuthProviderId names a provider the document declares; both fields inherit on their\n            own, so a host can turn an inherited provider off without forgetting which one it was, and a route can inherit its host's, name another or turn\n            it off (the route that serves the provider's own sign-in pages has to). A layer that names a provider without stating bool? AuthProviderSettings.Enabled\n            leaves the switch to the built-in default, which is on."
      },
      "BackupRequest": {
        "required": [
          "passphrase"
        ],
        "type": "object",
        "properties": {
          "passphrase": {
            "type": "string",
            "description": "At least 12 characters; only you know it, and nothing can recover a lost one."
          }
        },
        "additionalProperties": false,
        "description": "Asks for a full backup, encrypted under a passphrase the archive is useless without."
      },
      "CaRootCertificate": {
        "required": [
          "sha256Fingerprint",
          "subject",
          "notBefore",
          "notAfter"
        ],
        "type": "object",
        "properties": {
          "sha256Fingerprint": {
            "type": "string",
            "description": "SHA-256 of the root's DER encoding, lowercase hex without separators."
          },
          "subject": {
            "type": "string",
            "description": "The root's subject distinguished name, RFC 4514 style (e.g. `CN=Paduraksa Local CA (1a2b3c4d)`)."
          },
          "notBefore": {
            "type": "string",
            "description": "Start of validity.",
            "format": "date-time"
          },
          "notAfter": {
            "type": "string",
            "description": "End of validity.",
            "format": "date-time"
          }
        },
        "additionalProperties": false,
        "description": "The local CA root as the download endpoints describe it: public data only, so the first-run wizard\ncan show the fingerprint of the certificate the browser is asked to trust, next to the certificate itself."
      },
      "CertificateBinding": {
        "type": "object",
        "properties": {
          "mode": {
            "$ref": "#/components/schemas/CertificateBindingMode"
          },
          "certificateId": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Set exactly when CertificateBindingMode CertificateBinding.Mode is CertificateBindingMode.Specific.",
                "$ref": "#/components/schemas/CertificateId"
              }
            ]
          }
        },
        "additionalProperties": false,
        "description": "A host's certificate binding."
      },
      "CertificateBindingMode": {
        "enum": [
          "auto",
          "specific",
          "none"
        ],
        "type": "string",
        "description": "How a host picks its certificate."
      },
      "CertificateDetail": {
        "required": [
          "summary",
          "chainIssuers",
          "publicChainPem"
        ],
        "type": "object",
        "properties": {
          "summary": {
            "description": "The listing fields.",
            "$ref": "#/components/schemas/CertificateSummary"
          },
          "chainIssuers": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The issuer DN of every certificate in the chain, in order."
          },
          "publicChainPem": {
            "type": "string",
            "description": "The leaf and its intermediates as PEM. Public."
          }
        },
        "additionalProperties": false,
        "description": "A certificate with its public chain, for the detail view and the chain download."
      },
      "CertificateId": {
        "examples": [
          "crt_0A1b2C3d4E5f6G7h8I9j0K"
        ],
        "pattern": "^crt_[0-9A-Za-z]{22}$",
        "type": [
          "null",
          "string"
        ],
        "description": "A CertificateId: 'crt_' and 22 base62 characters."
      },
      "CertificateIssueRequest": {
        "required": [
          "names",
          "accountId"
        ],
        "type": "object",
        "properties": {
          "names": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "DNS names (wildcards as `*.parent`) and IP addresses; at least one. HTTP-01 cannot prove a wildcard, DNS-01 cannot prove an IP address."
          },
          "accountId": {
            "description": "The ACME account to order with (active).",
            "$ref": "#/components/schemas/AcmeAccountId"
          },
          "challengeType": {
            "enum": [
              "http-01",
              "dns-01"
            ],
            "type": "string",
            "description": "How the names are proven.",
            "default": "http-01"
          },
          "keySpec": {
            "enum": [
              "ecdsa-p256",
              "ecdsa-p384",
              "rsa-2048",
              "rsa-3072",
              "rsa-4096"
            ],
            "type": "string",
            "description": "The kind of key the vault generates for the certificate.",
            "default": "ecdsa-p256"
          },
          "dnsProviderId": {
            "examples": [
              "dnsp_0A1b2C3d4E5f6G7h8I9j0K"
            ],
            "pattern": "^dnsp_[0-9A-Za-z]{22}$",
            "type": [
              "null",
              "string"
            ],
            "description": "The DNS provider for DNS-01; refused for HTTP-01."
          },
          "label": {
            "type": [
              "null",
              "string"
            ],
            "description": "A display name for the certificate."
          },
          "profile": {
            "type": [
              "null",
              "string"
            ],
            "description": "An ACME profile of the CA (for example `shortlived`); null for its default."
          },
          "preferredChain": {
            "type": [
              "null",
              "string"
            ],
            "description": "The issuer common name the chain should end in; null for the CA's default chain."
          }
        },
        "additionalProperties": false,
        "description": "Asks for a certificate from an ACME CA (`AcmeIssuanceService.RequestCertificateAsync`)."
      },
      "CertificateOperationResult": {
        "required": [
          "status"
        ],
        "type": "object",
        "properties": {
          "status": {
            "description": "How it ended.",
            "$ref": "#/components/schemas/CertificateOperationStatus"
          },
          "certificate": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "The certificate after the operation; null for a delete, or when it didn't complete.",
                "$ref": "#/components/schemas/CertificateSummary"
              }
            ]
          },
          "publish": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "How publishing the recompiled config went, when the operation affected serving.",
                "$ref": "#/components/schemas/PublishOutcome"
              }
            ]
          },
          "errors": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/ConfigError"
            }
          },
          "warnings": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/ConfigError"
            }
          }
        },
        "additionalProperties": false,
        "description": "The outcome of a certificate operation."
      },
      "CertificateOperationStatus": {
        "enum": [
          "completed",
          "unchanged",
          "notFound",
          "invalid",
          "forbidden",
          "unavailable"
        ],
        "type": "string",
        "description": "How a certificate operation ended."
      },
      "CertificateSource": {
        "enum": [
          "acme",
          "uploaded",
          "self_signed",
          "local_ca",
          "local_ca_root"
        ],
        "type": "string",
        "description": "Where a certificate came from. Persisted by name (ModelNames)."
      },
      "CertificateStatus": {
        "enum": [
          "pending",
          "active",
          "failed"
        ],
        "type": "string",
        "description": "The lifecycle state of a certificate's metadata row. Expiry is not a state: it follows from\n            `NotAfter` and the clock, so nothing has to flip a row when a certificate runs out."
      },
      "CertificateSummary": {
        "required": [
          "id",
          "source",
          "status",
          "validity",
          "names",
          "notBefore",
          "notAfter",
          "subject",
          "issuer",
          "serialNumber",
          "sha256Fingerprint",
          "keyAlgorithm",
          "createdAt",
          "updatedAt"
        ],
        "type": "object",
        "properties": {
          "id": {
            "description": "The certificate.",
            "$ref": "#/components/schemas/CertificateId"
          },
          "source": {
            "description": "Where it came from.",
            "$ref": "#/components/schemas/CertificateSource"
          },
          "status": {
            "description": "The stored state.",
            "$ref": "#/components/schemas/CertificateStatus"
          },
          "validity": {
            "description": "Against the clock when the summary was made.",
            "$ref": "#/components/schemas/CertificateValidity"
          },
          "names": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The names it covers (DNS names, `*.parent` wildcards, IP addresses), normalised."
          },
          "notBefore": {
            "type": "string",
            "description": "Start of validity.",
            "format": "date-time"
          },
          "notAfter": {
            "type": "string",
            "description": "End of validity.",
            "format": "date-time"
          },
          "subject": {
            "type": "string",
            "description": "The subject DN, or empty."
          },
          "issuer": {
            "type": "string",
            "description": "The issuer DN."
          },
          "serialNumber": {
            "type": "string",
            "description": "Hex, as string CertificateInfo.SerialNumber."
          },
          "sha256Fingerprint": {
            "type": "string",
            "description": "Of the leaf, lowercase hex."
          },
          "keyAlgorithm": {
            "type": "string",
            "description": "A display label, e.g. `ECDSA P-256`."
          },
          "createdAt": {
            "type": "string",
            "description": "When the ID was first registered.",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "description": "When the metadata last changed.",
            "format": "date-time"
          },
          "statusMessage": {
            "type": [
              "null",
              "string"
            ],
            "description": "Why it failed, for CertificateStatus.Failed."
          },
          "authorityKeyIdentifier": {
            "type": [
              "null",
              "string"
            ],
            "description": "Hex, or null."
          },
          "label": {
            "type": [
              "null",
              "string"
            ],
            "description": "A display name, or null."
          },
          "notes": {
            "type": [
              "null",
              "string"
            ],
            "description": "Free text, or null."
          }
        },
        "additionalProperties": false,
        "description": "A certificate as listed: everything except the chain."
      },
      "CertificateValidity": {
        "enum": [
          "notYetValid",
          "valid",
          "expired"
        ],
        "type": "string",
        "description": "Where a certificate stands against the clock. Derived from `NotBefore`/`NotAfter` at the time of\nthe query, never stored."
      },
      "ChangePasswordRequest": {
        "required": [
          "currentPassword",
          "newPassword"
        ],
        "type": "object",
        "properties": {
          "currentPassword": {
            "type": "string",
            "description": "Proves the person at the keyboard knows it."
          },
          "newPassword": {
            "type": "string",
            "description": "The new password."
          }
        },
        "additionalProperties": false,
        "description": "Changes the signed-in user's own password."
      },
      "Channel": {
        "enum": [
          "system",
          "cli",
          "dashboard",
          "api",
          "mcp"
        ],
        "type": "string",
        "description": "The surface a change or audited action came in through."
      },
      "CloudflareSettings": {
        "type": "object",
        "properties": {
          "originLockdown": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Refuse a request whose connection peer isn't Cloudflare's edge (`CloudflareProxy`) or the configured\n`cloudflared` address (`CloudflareTunnel`), before anything else the host does. Built-in default: off. When\nevery host in the config has it on, such a peer is dropped before the TLS handshake whichever mode is chosen.",
                "$ref": "#/components/schemas/OriginLockdownMode"
              }
            ]
          },
          "authenticatedOriginPulls": {
            "type": [
              "null",
              "boolean"
            ],
            "description": "Require Cloudflare's client certificate (Authenticated Origin Pulls) on the TLS handshake, checked against\nCloudflare's origin-pull CA. Built-in default: off. Cloudflare doesn't connect to origins over HTTP/3, so a host with\nthis on is not served over it, and it needs the setting turned on in the zone's Cloudflare dashboard too."
          }
        },
        "additionalProperties": false,
        "description": "Keeping the host reachable through Cloudflare only. Both fields do nothing unless the global trusted-proxy mode is\n`CloudflareProxy` or `CloudflareTunnel` (the config warns when they are set without it). Each field inherits on\nits own."
      },
      "CompressionSettings": {
        "type": "object",
        "properties": {
          "enabled": {
            "type": [
              "null",
              "boolean"
            ],
            "description": "Compress. Built-in default: off: a response that carries a secret next to input an attacker controls leaks the\n            secret through its compressed size (BREACH), and the proxy can't tell which responses do."
          },
          "minimumSize": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "description": "The smallest declared body that is compressed, in bytes, 0 to 1 MiB; a body without a declared length is always\n            compressed. Built-in default: 1 KiB, below which the framing outweighs the saving.",
            "format": "int32"
          }
        },
        "additionalProperties": false,
        "description": "Compressing the upstream's responses with Brotli or gzip, for a client whose `Accept-Encoding` takes one. Only a\ntext-like content type (HTML, CSS, JavaScript, JSON, XML, SVG, plain text, WebAssembly) over int? CompressionSettings.MinimumSize, and\nnever a response the upstream already encoded, a range, an event stream or the proxy's own answers. Each field inherits on\nits own."
      },
      "ConfigChange": {
        "required": [
          "object"
        ],
        "type": "object",
        "anyOf": [
          {
            "$ref": "#/components/schemas/ConfigChangeDocumentChange"
          },
          {
            "$ref": "#/components/schemas/ConfigChangeSettingsChange"
          },
          {
            "$ref": "#/components/schemas/ConfigChangeProfileChange"
          },
          {
            "$ref": "#/components/schemas/ConfigChangeUpstreamChange"
          },
          {
            "$ref": "#/components/schemas/ConfigChangeDestinationChange"
          },
          {
            "$ref": "#/components/schemas/ConfigChangeRateLimitPolicyChange"
          },
          {
            "$ref": "#/components/schemas/ConfigChangeAccessListChange"
          },
          {
            "$ref": "#/components/schemas/ConfigChangeRuleSetChange"
          },
          {
            "$ref": "#/components/schemas/ConfigChangeAuthProviderChange"
          },
          {
            "$ref": "#/components/schemas/ConfigChangeHostChange"
          },
          {
            "$ref": "#/components/schemas/ConfigChangeRouteChange"
          }
        ],
        "description": "One entry of the structural diff between two config documents: an object added, removed or modified,\nkeyed by its typed ID. It feeds the dashboard's review sheet, `dryRun` responses and revision summaries.",
        "discriminator": {
          "propertyName": "object",
          "mapping": {
            "document": "#/components/schemas/ConfigChangeDocumentChange",
            "settings": "#/components/schemas/ConfigChangeSettingsChange",
            "profile": "#/components/schemas/ConfigChangeProfileChange",
            "upstream": "#/components/schemas/ConfigChangeUpstreamChange",
            "destination": "#/components/schemas/ConfigChangeDestinationChange",
            "rateLimitPolicy": "#/components/schemas/ConfigChangeRateLimitPolicyChange",
            "accessList": "#/components/schemas/ConfigChangeAccessListChange",
            "ruleSet": "#/components/schemas/ConfigChangeRuleSetChange",
            "authProvider": "#/components/schemas/ConfigChangeAuthProviderChange",
            "host": "#/components/schemas/ConfigChangeHostChange",
            "route": "#/components/schemas/ConfigChangeRouteChange"
          }
        }
      },
      "ConfigChangeAccessListChange": {
        "required": [
          "accessListId",
          "kind",
          "path",
          "fields"
        ],
        "properties": {
          "object": {
            "enum": [
              "accessList"
            ],
            "type": "string"
          },
          "accessListId": {
            "$ref": "#/components/schemas/AccessListId"
          },
          "kind": {
            "description": "What happened to the object.",
            "$ref": "#/components/schemas/ConfigChangeKind"
          },
          "path": {
            "type": "string",
            "description": "JSON pointer of the object: into the new document for added and modified objects, into the old\n            one for removed objects."
          },
          "fields": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Modified only: JSON pointers (into the new document) of the fields that changed, in\n            document order. A list counts as one field, so it is named as a whole; so is a nested object that is set on one side\n            only. Child objects (routes, destinations) aren't fields of their parent."
          }
        },
        "additionalProperties": false
      },
      "ConfigChangeAuthProviderChange": {
        "required": [
          "authProviderId",
          "kind",
          "path",
          "fields"
        ],
        "properties": {
          "object": {
            "enum": [
              "authProvider"
            ],
            "type": "string"
          },
          "authProviderId": {
            "$ref": "#/components/schemas/AuthProviderId"
          },
          "kind": {
            "description": "What happened to the object.",
            "$ref": "#/components/schemas/ConfigChangeKind"
          },
          "path": {
            "type": "string",
            "description": "JSON pointer of the object: into the new document for added and modified objects, into the old\n            one for removed objects."
          },
          "fields": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Modified only: JSON pointers (into the new document) of the fields that changed, in\n            document order. A list counts as one field, so it is named as a whole; so is a nested object that is set on one side\n            only. Child objects (routes, destinations) aren't fields of their parent."
          }
        },
        "additionalProperties": false
      },
      "ConfigChangeDestinationChange": {
        "required": [
          "upstreamId",
          "destinationId",
          "kind",
          "path",
          "fields"
        ],
        "properties": {
          "object": {
            "enum": [
              "destination"
            ],
            "type": "string"
          },
          "upstreamId": {
            "$ref": "#/components/schemas/UpstreamId"
          },
          "destinationId": {
            "$ref": "#/components/schemas/DestinationId"
          },
          "kind": {
            "description": "What happened to the object.",
            "$ref": "#/components/schemas/ConfigChangeKind"
          },
          "path": {
            "type": "string",
            "description": "JSON pointer of the object: into the new document for added and modified objects, into the old\n            one for removed objects."
          },
          "fields": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Modified only: JSON pointers (into the new document) of the fields that changed, in\n            document order. A list counts as one field, so it is named as a whole; so is a nested object that is set on one side\n            only. Child objects (routes, destinations) aren't fields of their parent."
          }
        },
        "additionalProperties": false
      },
      "ConfigChangeDocumentChange": {
        "required": [
          "kind",
          "path",
          "fields"
        ],
        "properties": {
          "object": {
            "enum": [
              "document"
            ],
            "type": "string"
          },
          "kind": {
            "description": "What happened to the object.",
            "$ref": "#/components/schemas/ConfigChangeKind"
          },
          "path": {
            "type": "string",
            "description": "JSON pointer of the object: into the new document for added and modified objects, into the old\n            one for removed objects."
          },
          "fields": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Modified only: JSON pointers (into the new document) of the fields that changed, in\n            document order. A list counts as one field, so it is named as a whole; so is a nested object that is set on one side\n            only. Child objects (routes, destinations) aren't fields of their parent."
          }
        },
        "additionalProperties": false,
        "description": "The document itself changed: its `apiVersion` (always ConfigChangeKind.Modified, path\n            `\"\"`)."
      },
      "ConfigChangeHostChange": {
        "required": [
          "hostId",
          "kind",
          "path",
          "fields"
        ],
        "properties": {
          "object": {
            "enum": [
              "host"
            ],
            "type": "string"
          },
          "hostId": {
            "$ref": "#/components/schemas/HostId"
          },
          "kind": {
            "description": "What happened to the object.",
            "$ref": "#/components/schemas/ConfigChangeKind"
          },
          "path": {
            "type": "string",
            "description": "JSON pointer of the object: into the new document for added and modified objects, into the old\n            one for removed objects."
          },
          "fields": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Modified only: JSON pointers (into the new document) of the fields that changed, in\n            document order. A list counts as one field, so it is named as a whole; so is a nested object that is set on one side\n            only. Child objects (routes, destinations) aren't fields of their parent."
          }
        },
        "additionalProperties": false
      },
      "ConfigChangeKind": {
        "enum": [
          "added",
          "removed",
          "modified"
        ],
        "type": "string",
        "description": "What happened to an object."
      },
      "ConfigChangeProfileChange": {
        "required": [
          "profileId",
          "kind",
          "path",
          "fields"
        ],
        "properties": {
          "object": {
            "enum": [
              "profile"
            ],
            "type": "string"
          },
          "profileId": {
            "$ref": "#/components/schemas/ProfileId"
          },
          "kind": {
            "description": "What happened to the object.",
            "$ref": "#/components/schemas/ConfigChangeKind"
          },
          "path": {
            "type": "string",
            "description": "JSON pointer of the object: into the new document for added and modified objects, into the old\n            one for removed objects."
          },
          "fields": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Modified only: JSON pointers (into the new document) of the fields that changed, in\n            document order. A list counts as one field, so it is named as a whole; so is a nested object that is set on one side\n            only. Child objects (routes, destinations) aren't fields of their parent."
          }
        },
        "additionalProperties": false
      },
      "ConfigChangeRateLimitPolicyChange": {
        "required": [
          "rateLimitPolicyId",
          "kind",
          "path",
          "fields"
        ],
        "properties": {
          "object": {
            "enum": [
              "rateLimitPolicy"
            ],
            "type": "string"
          },
          "rateLimitPolicyId": {
            "$ref": "#/components/schemas/RateLimitPolicyId"
          },
          "kind": {
            "description": "What happened to the object.",
            "$ref": "#/components/schemas/ConfigChangeKind"
          },
          "path": {
            "type": "string",
            "description": "JSON pointer of the object: into the new document for added and modified objects, into the old\n            one for removed objects."
          },
          "fields": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Modified only: JSON pointers (into the new document) of the fields that changed, in\n            document order. A list counts as one field, so it is named as a whole; so is a nested object that is set on one side\n            only. Child objects (routes, destinations) aren't fields of their parent."
          }
        },
        "additionalProperties": false
      },
      "ConfigChangeRouteChange": {
        "required": [
          "hostId",
          "routeId",
          "kind",
          "path",
          "fields"
        ],
        "properties": {
          "object": {
            "enum": [
              "route"
            ],
            "type": "string"
          },
          "hostId": {
            "$ref": "#/components/schemas/HostId"
          },
          "routeId": {
            "$ref": "#/components/schemas/RouteId"
          },
          "kind": {
            "description": "What happened to the object.",
            "$ref": "#/components/schemas/ConfigChangeKind"
          },
          "path": {
            "type": "string",
            "description": "JSON pointer of the object: into the new document for added and modified objects, into the old\n            one for removed objects."
          },
          "fields": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Modified only: JSON pointers (into the new document) of the fields that changed, in\n            document order. A list counts as one field, so it is named as a whole; so is a nested object that is set on one side\n            only. Child objects (routes, destinations) aren't fields of their parent."
          }
        },
        "additionalProperties": false
      },
      "ConfigChangeRuleSetChange": {
        "required": [
          "ruleSetId",
          "kind",
          "path",
          "fields"
        ],
        "properties": {
          "object": {
            "enum": [
              "ruleSet"
            ],
            "type": "string"
          },
          "ruleSetId": {
            "$ref": "#/components/schemas/RuleSetId"
          },
          "kind": {
            "description": "What happened to the object.",
            "$ref": "#/components/schemas/ConfigChangeKind"
          },
          "path": {
            "type": "string",
            "description": "JSON pointer of the object: into the new document for added and modified objects, into the old\n            one for removed objects."
          },
          "fields": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Modified only: JSON pointers (into the new document) of the fields that changed, in\n            document order. A list counts as one field, so it is named as a whole; so is a nested object that is set on one side\n            only. Child objects (routes, destinations) aren't fields of their parent."
          }
        },
        "additionalProperties": false
      },
      "ConfigChangeSettingsChange": {
        "required": [
          "kind",
          "path",
          "fields"
        ],
        "properties": {
          "object": {
            "enum": [
              "settings"
            ],
            "type": "string"
          },
          "kind": {
            "description": "What happened to the object.",
            "$ref": "#/components/schemas/ConfigChangeKind"
          },
          "path": {
            "type": "string",
            "description": "JSON pointer of the object: into the new document for added and modified objects, into the old\n            one for removed objects."
          },
          "fields": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Modified only: JSON pointers (into the new document) of the fields that changed, in\n            document order. A list counts as one field, so it is named as a whole; so is a nested object that is set on one side\n            only. Child objects (routes, destinations) aren't fields of their parent."
          }
        },
        "additionalProperties": false,
        "description": "The global settings changed (always ConfigChangeKind.Modified)."
      },
      "ConfigChangeUpstreamChange": {
        "required": [
          "upstreamId",
          "kind",
          "path",
          "fields"
        ],
        "properties": {
          "object": {
            "enum": [
              "upstream"
            ],
            "type": "string"
          },
          "upstreamId": {
            "$ref": "#/components/schemas/UpstreamId"
          },
          "kind": {
            "description": "What happened to the object.",
            "$ref": "#/components/schemas/ConfigChangeKind"
          },
          "path": {
            "type": "string",
            "description": "JSON pointer of the object: into the new document for added and modified objects, into the old\n            one for removed objects."
          },
          "fields": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Modified only: JSON pointers (into the new document) of the fields that changed, in\n            document order. A list counts as one field, so it is named as a whole; so is a nested object that is set on one side\n            only. Child objects (routes, destinations) aren't fields of their parent."
          }
        },
        "additionalProperties": false
      },
      "ConfigCommand": {
        "required": [
          "command"
        ],
        "type": "object",
        "anyOf": [
          {
            "$ref": "#/components/schemas/ConfigCommandUpsertHostCommand"
          },
          {
            "$ref": "#/components/schemas/ConfigCommandDeleteHostCommand"
          },
          {
            "$ref": "#/components/schemas/ConfigCommandUpsertUpstreamCommand"
          },
          {
            "$ref": "#/components/schemas/ConfigCommandDeleteUpstreamCommand"
          },
          {
            "$ref": "#/components/schemas/ConfigCommandUpsertRateLimitPolicyCommand"
          },
          {
            "$ref": "#/components/schemas/ConfigCommandDeleteRateLimitPolicyCommand"
          },
          {
            "$ref": "#/components/schemas/ConfigCommandUpsertAccessListCommand"
          },
          {
            "$ref": "#/components/schemas/ConfigCommandDeleteAccessListCommand"
          },
          {
            "$ref": "#/components/schemas/ConfigCommandUpsertRuleSetCommand"
          },
          {
            "$ref": "#/components/schemas/ConfigCommandDeleteRuleSetCommand"
          },
          {
            "$ref": "#/components/schemas/ConfigCommandUpsertAuthProviderCommand"
          },
          {
            "$ref": "#/components/schemas/ConfigCommandDeleteAuthProviderCommand"
          },
          {
            "$ref": "#/components/schemas/ConfigCommandUpsertProfileCommand"
          },
          {
            "$ref": "#/components/schemas/ConfigCommandDeleteProfileCommand"
          },
          {
            "$ref": "#/components/schemas/ConfigCommandUpdateSettingsCommand"
          },
          {
            "$ref": "#/components/schemas/ConfigCommandReplaceConfigCommand"
          },
          {
            "$ref": "#/components/schemas/ConfigCommandRollbackConfigCommand"
          }
        ],
        "description": "A change to the config, as every surface (dashboard, API, MCP, CLI, YAML import) hands it to the\ncommand pipeline. Who sent it is not part of the command: the surface passes the authenticated origin alongside.",
        "discriminator": {
          "propertyName": "command",
          "mapping": {
            "upsertHost": "#/components/schemas/ConfigCommandUpsertHostCommand",
            "deleteHost": "#/components/schemas/ConfigCommandDeleteHostCommand",
            "upsertUpstream": "#/components/schemas/ConfigCommandUpsertUpstreamCommand",
            "deleteUpstream": "#/components/schemas/ConfigCommandDeleteUpstreamCommand",
            "upsertRateLimitPolicy": "#/components/schemas/ConfigCommandUpsertRateLimitPolicyCommand",
            "deleteRateLimitPolicy": "#/components/schemas/ConfigCommandDeleteRateLimitPolicyCommand",
            "upsertAccessList": "#/components/schemas/ConfigCommandUpsertAccessListCommand",
            "deleteAccessList": "#/components/schemas/ConfigCommandDeleteAccessListCommand",
            "upsertRuleSet": "#/components/schemas/ConfigCommandUpsertRuleSetCommand",
            "deleteRuleSet": "#/components/schemas/ConfigCommandDeleteRuleSetCommand",
            "upsertAuthProvider": "#/components/schemas/ConfigCommandUpsertAuthProviderCommand",
            "deleteAuthProvider": "#/components/schemas/ConfigCommandDeleteAuthProviderCommand",
            "upsertProfile": "#/components/schemas/ConfigCommandUpsertProfileCommand",
            "deleteProfile": "#/components/schemas/ConfigCommandDeleteProfileCommand",
            "updateSettings": "#/components/schemas/ConfigCommandUpdateSettingsCommand",
            "replaceConfig": "#/components/schemas/ConfigCommandReplaceConfigCommand",
            "rollbackConfig": "#/components/schemas/ConfigCommandRollbackConfigCommand"
          }
        }
      },
      "ConfigCommandDeleteAccessListCommand": {
        "required": [
          "accessListId",
          "version"
        ],
        "properties": {
          "command": {
            "enum": [
              "deleteAccessList"
            ],
            "type": "string"
          },
          "accessListId": {
            "description": "The list.",
            "$ref": "#/components/schemas/AccessListId"
          },
          "version": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "The version the caller saw.",
            "format": "int64"
          },
          "dryRun": {
            "type": "boolean",
            "description": "Run every check (authorize, apply, validate, compile) and return the diff, but persist nothing."
          },
          "force": {
            "type": "boolean",
            "description": "Do the change even though it cuts the caller's own way into the admin. When a command arrives through the\nadmin's internal listener, a change that deletes the admin host, disables it or removes its HTTPS is refused unless\nthis is set (the API's `?force=true`, a command's `force`); on any other surface it has no effect."
          },
          "message": {
            "type": [
              "null",
              "string"
            ],
            "description": "An optional note stored with the revision (a commit message). At most 1000 characters."
          }
        },
        "additionalProperties": false,
        "description": "Deletes an access list; no host, profile, global default or route may still name it."
      },
      "ConfigCommandDeleteAuthProviderCommand": {
        "required": [
          "authProviderId",
          "version"
        ],
        "properties": {
          "command": {
            "enum": [
              "deleteAuthProvider"
            ],
            "type": "string"
          },
          "authProviderId": {
            "description": "The provider.",
            "$ref": "#/components/schemas/AuthProviderId"
          },
          "version": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "The version the caller saw.",
            "format": "int64"
          },
          "dryRun": {
            "type": "boolean",
            "description": "Run every check (authorize, apply, validate, compile) and return the diff, but persist nothing."
          },
          "force": {
            "type": "boolean",
            "description": "Do the change even though it cuts the caller's own way into the admin. When a command arrives through the\nadmin's internal listener, a change that deletes the admin host, disables it or removes its HTTPS is refused unless\nthis is set (the API's `?force=true`, a command's `force`); on any other surface it has no effect."
          },
          "message": {
            "type": [
              "null",
              "string"
            ],
            "description": "An optional note stored with the revision (a commit message). At most 1000 characters."
          }
        },
        "additionalProperties": false,
        "description": "Deletes an auth provider; no host may still resolve to it, directly, through its profile or the global defaults, and no route may name it."
      },
      "ConfigCommandDeleteHostCommand": {
        "required": [
          "hostId",
          "version"
        ],
        "properties": {
          "command": {
            "enum": [
              "deleteHost"
            ],
            "type": "string"
          },
          "hostId": {
            "description": "The host.",
            "$ref": "#/components/schemas/HostId"
          },
          "version": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "The version the caller saw.",
            "format": "int64"
          },
          "dryRun": {
            "type": "boolean",
            "description": "Run every check (authorize, apply, validate, compile) and return the diff, but persist nothing."
          },
          "force": {
            "type": "boolean",
            "description": "Do the change even though it cuts the caller's own way into the admin. When a command arrives through the\nadmin's internal listener, a change that deletes the admin host, disables it or removes its HTTPS is refused unless\nthis is set (the API's `?force=true`, a command's `force`); on any other surface it has no effect."
          },
          "message": {
            "type": [
              "null",
              "string"
            ],
            "description": "An optional note stored with the revision (a commit message). At most 1000 characters."
          }
        },
        "additionalProperties": false,
        "description": "Deletes a host and its routes."
      },
      "ConfigCommandDeleteProfileCommand": {
        "required": [
          "profileId",
          "version"
        ],
        "properties": {
          "command": {
            "enum": [
              "deleteProfile"
            ],
            "type": "string"
          },
          "profileId": {
            "description": "The profile.",
            "$ref": "#/components/schemas/ProfileId"
          },
          "version": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "The version the caller saw.",
            "format": "int64"
          },
          "dryRun": {
            "type": "boolean",
            "description": "Run every check (authorize, apply, validate, compile) and return the diff, but persist nothing."
          },
          "force": {
            "type": "boolean",
            "description": "Do the change even though it cuts the caller's own way into the admin. When a command arrives through the\nadmin's internal listener, a change that deletes the admin host, disables it or removes its HTTPS is refused unless\nthis is set (the API's `?force=true`, a command's `force`); on any other surface it has no effect."
          },
          "message": {
            "type": [
              "null",
              "string"
            ],
            "description": "An optional note stored with the revision (a commit message). At most 1000 characters."
          }
        },
        "additionalProperties": false,
        "description": "Deletes a profile; no host may still use it."
      },
      "ConfigCommandDeleteRateLimitPolicyCommand": {
        "required": [
          "rateLimitPolicyId",
          "version"
        ],
        "properties": {
          "command": {
            "enum": [
              "deleteRateLimitPolicy"
            ],
            "type": "string"
          },
          "rateLimitPolicyId": {
            "description": "The policy.",
            "$ref": "#/components/schemas/RateLimitPolicyId"
          },
          "version": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "The version the caller saw.",
            "format": "int64"
          },
          "dryRun": {
            "type": "boolean",
            "description": "Run every check (authorize, apply, validate, compile) and return the diff, but persist nothing."
          },
          "force": {
            "type": "boolean",
            "description": "Do the change even though it cuts the caller's own way into the admin. When a command arrives through the\nadmin's internal listener, a change that deletes the admin host, disables it or removes its HTTPS is refused unless\nthis is set (the API's `?force=true`, a command's `force`); on any other surface it has no effect."
          },
          "message": {
            "type": [
              "null",
              "string"
            ],
            "description": "An optional note stored with the revision (a commit message). At most 1000 characters."
          }
        },
        "additionalProperties": false,
        "description": "Deletes a rate-limit policy; no host or route may still name it."
      },
      "ConfigCommandDeleteRuleSetCommand": {
        "required": [
          "ruleSetId",
          "version"
        ],
        "properties": {
          "command": {
            "enum": [
              "deleteRuleSet"
            ],
            "type": "string"
          },
          "ruleSetId": {
            "description": "The set.",
            "$ref": "#/components/schemas/RuleSetId"
          },
          "version": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "The version the caller saw.",
            "format": "int64"
          },
          "dryRun": {
            "type": "boolean",
            "description": "Run every check (authorize, apply, validate, compile) and return the diff, but persist nothing."
          },
          "force": {
            "type": "boolean",
            "description": "Do the change even though it cuts the caller's own way into the admin. When a command arrives through the\nadmin's internal listener, a change that deletes the admin host, disables it or removes its HTTPS is refused unless\nthis is set (the API's `?force=true`, a command's `force`); on any other surface it has no effect."
          },
          "message": {
            "type": [
              "null",
              "string"
            ],
            "description": "An optional note stored with the revision (a commit message). At most 1000 characters."
          }
        },
        "additionalProperties": false,
        "description": "Deletes a rule set; no host may still resolve to it, directly, through its profile or the global defaults, and no route may name it."
      },
      "ConfigCommandDeleteUpstreamCommand": {
        "required": [
          "upstreamId",
          "version"
        ],
        "properties": {
          "command": {
            "enum": [
              "deleteUpstream"
            ],
            "type": "string"
          },
          "upstreamId": {
            "description": "The upstream.",
            "$ref": "#/components/schemas/UpstreamId"
          },
          "version": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "The version the caller saw.",
            "format": "int64"
          },
          "dryRun": {
            "type": "boolean",
            "description": "Run every check (authorize, apply, validate, compile) and return the diff, but persist nothing."
          },
          "force": {
            "type": "boolean",
            "description": "Do the change even though it cuts the caller's own way into the admin. When a command arrives through the\nadmin's internal listener, a change that deletes the admin host, disables it or removes its HTTPS is refused unless\nthis is set (the API's `?force=true`, a command's `force`); on any other surface it has no effect."
          },
          "message": {
            "type": [
              "null",
              "string"
            ],
            "description": "An optional note stored with the revision (a commit message). At most 1000 characters."
          }
        },
        "additionalProperties": false,
        "description": "Deletes an upstream and its destinations; no route may still use it."
      },
      "ConfigCommandReplaceConfigCommand": {
        "required": [
          "document"
        ],
        "properties": {
          "command": {
            "enum": [
              "replaceConfig"
            ],
            "type": "string"
          },
          "document": {
            "description": "The config as it should be.",
            "$ref": "#/components/schemas/ConfigDocument"
          },
          "baseRevision": {
            "examples": [
              "rev_0A1b2C3d4E5f6G7h8I9j0K"
            ],
            "pattern": "^rev_[0-9A-Za-z]{22}$",
            "type": [
              "null",
              "string"
            ],
            "description": "The revision the document was based on: must be the latest. Null only while there is\n            no revision yet."
          },
          "dryRun": {
            "type": "boolean",
            "description": "Run every check (authorize, apply, validate, compile) and return the diff, but persist nothing."
          },
          "force": {
            "type": "boolean",
            "description": "Do the change even though it cuts the caller's own way into the admin. When a command arrives through the\nadmin's internal listener, a change that deletes the admin host, disables it or removes its HTTPS is refused unless\nthis is set (the API's `?force=true`, a command's `force`); on any other surface it has no effect."
          },
          "message": {
            "type": [
              "null",
              "string"
            ],
            "description": "An optional note stored with the revision (a commit message). At most 1000 characters."
          }
        },
        "additionalProperties": false,
        "description": "Replaces the whole document: the import path. The versions in Document are ignored:\nan object whose config doesn't change keeps its version, every other one gets a new one."
      },
      "ConfigCommandResult": {
        "required": [
          "status"
        ],
        "type": "object",
        "properties": {
          "status": {
            "description": "How it ended.",
            "$ref": "#/components/schemas/ConfigCommandStatus"
          },
          "revision": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "The revision committed (ConfigCommandStatus.Committed,\n            ConfigCommandStatus.PublishFailed); otherwise null.",
                "$ref": "#/components/schemas/RevisionRef"
              }
            ]
          },
          "publish": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "How publishing went: set exactly when a revision was committed.",
                "$ref": "#/components/schemas/PublishOutcome"
              }
            ]
          },
          "conflict": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Set exactly for ConfigCommandStatus.Conflict.",
                "$ref": "#/components/schemas/ConfigConflict"
              }
            ]
          },
          "version": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "description": "Object commands that upsert: the object's version after the change (committed or, for a dry\n            run, the one it would get).",
            "format": "int64"
          },
          "warnings": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/ConfigWarning"
            }
          },
          "changes": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/ConfigChange"
            }
          },
          "errors": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/ConfigError"
            }
          },
          "relatedErrors": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/ConfigError"
            }
          }
        },
        "additionalProperties": false,
        "description": "The outcome of a ConfigCommand."
      },
      "ConfigCommandRollbackConfigCommand": {
        "required": [
          "target",
          "baseRevision"
        ],
        "properties": {
          "command": {
            "enum": [
              "rollbackConfig"
            ],
            "type": "string"
          },
          "target": {
            "description": "The revision whose config to restore.",
            "$ref": "#/components/schemas/ConfigRevisionId"
          },
          "baseRevision": {
            "description": "The latest revision as the caller saw it: must still be the latest.",
            "$ref": "#/components/schemas/ConfigRevisionId"
          },
          "dryRun": {
            "type": "boolean",
            "description": "Run every check (authorize, apply, validate, compile) and return the diff, but persist nothing."
          },
          "force": {
            "type": "boolean",
            "description": "Do the change even though it cuts the caller's own way into the admin. When a command arrives through the\nadmin's internal listener, a change that deletes the admin host, disables it or removes its HTTPS is refused unless\nthis is set (the API's `?force=true`, a command's `force`); on any other surface it has no effect."
          },
          "message": {
            "type": [
              "null",
              "string"
            ],
            "description": "An optional note stored with the revision (a commit message). At most 1000 characters."
          }
        },
        "additionalProperties": false,
        "description": "Restores the config of an earlier revision as a <b>new</b> revision; history is never rewritten. The\ndefault string? ConfigCommand.Message is \"Rollback to #N\"."
      },
      "ConfigCommandStatus": {
        "enum": [
          "committed",
          "dryRun",
          "unchanged",
          "invalid",
          "conflict",
          "notFound",
          "denied",
          "publishFailed",
          "unavailable"
        ],
        "type": "string",
        "description": "How a ConfigCommand ended."
      },
      "ConfigCommandUpdateSettingsCommand": {
        "required": [
          "settings"
        ],
        "properties": {
          "command": {
            "enum": [
              "updateSettings"
            ],
            "type": "string"
          },
          "settings": {
            "description": "The settings as they should be. Their long GlobalSettings.Version is the version edited.",
            "$ref": "#/components/schemas/GlobalSettings"
          },
          "dryRun": {
            "type": "boolean",
            "description": "Run every check (authorize, apply, validate, compile) and return the diff, but persist nothing."
          },
          "force": {
            "type": "boolean",
            "description": "Do the change even though it cuts the caller's own way into the admin. When a command arrives through the\nadmin's internal listener, a change that deletes the admin host, disables it or removes its HTTPS is refused unless\nthis is set (the API's `?force=true`, a command's `force`); on any other surface it has no effect."
          },
          "message": {
            "type": [
              "null",
              "string"
            ],
            "description": "An optional note stored with the revision (a commit message). At most 1000 characters."
          }
        },
        "additionalProperties": false,
        "description": "Replaces the global settings (there is always exactly one; the empty config has version 0)."
      },
      "ConfigCommandUpsertAccessListCommand": {
        "required": [
          "accessList"
        ],
        "properties": {
          "command": {
            "enum": [
              "upsertAccessList"
            ],
            "type": "string"
          },
          "accessList": {
            "description": "The list as it should be. Its long AccessListConfig.Version is the version edited.",
            "$ref": "#/components/schemas/AccessListConfig"
          },
          "dryRun": {
            "type": "boolean",
            "description": "Run every check (authorize, apply, validate, compile) and return the diff, but persist nothing."
          },
          "force": {
            "type": "boolean",
            "description": "Do the change even though it cuts the caller's own way into the admin. When a command arrives through the\nadmin's internal listener, a change that deletes the admin host, disables it or removes its HTTPS is refused unless\nthis is set (the API's `?force=true`, a command's `force`); on any other surface it has no effect."
          },
          "message": {
            "type": [
              "null",
              "string"
            ],
            "description": "An optional note stored with the revision (a commit message). At most 1000 characters."
          }
        },
        "additionalProperties": false,
        "description": "Creates an access list (long AccessListConfig.Version 0) or replaces one. A user's\n            string? AccessListUserConfig.Password is hashed before anything else happens to the command; a user sent with\n            neither a password nor a hash keeps the hash the list already has for that name."
      },
      "ConfigCommandUpsertAuthProviderCommand": {
        "required": [
          "authProvider"
        ],
        "properties": {
          "command": {
            "enum": [
              "upsertAuthProvider"
            ],
            "type": "string"
          },
          "authProvider": {
            "description": "The provider as it should be. Its long AuthProviderConfig.Version is the version edited.",
            "$ref": "#/components/schemas/AuthProviderConfig"
          },
          "dryRun": {
            "type": "boolean",
            "description": "Run every check (authorize, apply, validate, compile) and return the diff, but persist nothing."
          },
          "force": {
            "type": "boolean",
            "description": "Do the change even though it cuts the caller's own way into the admin. When a command arrives through the\nadmin's internal listener, a change that deletes the admin host, disables it or removes its HTTPS is refused unless\nthis is set (the API's `?force=true`, a command's `force`); on any other surface it has no effect."
          },
          "message": {
            "type": [
              "null",
              "string"
            ],
            "description": "An optional note stored with the revision (a commit message). At most 1000 characters."
          }
        },
        "additionalProperties": false,
        "description": "Creates an auth provider (long AuthProviderConfig.Version 0) or replaces one."
      },
      "ConfigCommandUpsertHostCommand": {
        "required": [
          "host"
        ],
        "properties": {
          "command": {
            "enum": [
              "upsertHost"
            ],
            "type": "string"
          },
          "host": {
            "description": "The host as it should be. Its long HostConfig.Version is the version edited.",
            "$ref": "#/components/schemas/HostConfig"
          },
          "dryRun": {
            "type": "boolean",
            "description": "Run every check (authorize, apply, validate, compile) and return the diff, but persist nothing."
          },
          "force": {
            "type": "boolean",
            "description": "Do the change even though it cuts the caller's own way into the admin. When a command arrives through the\nadmin's internal listener, a change that deletes the admin host, disables it or removes its HTTPS is refused unless\nthis is set (the API's `?force=true`, a command's `force`); on any other surface it has no effect."
          },
          "message": {
            "type": [
              "null",
              "string"
            ],
            "description": "An optional note stored with the revision (a commit message). At most 1000 characters."
          }
        },
        "additionalProperties": false,
        "description": "Creates a host (long HostConfig.Version 0) or replaces one, routes included."
      },
      "ConfigCommandUpsertProfileCommand": {
        "required": [
          "profile"
        ],
        "properties": {
          "command": {
            "enum": [
              "upsertProfile"
            ],
            "type": "string"
          },
          "profile": {
            "description": "The profile as it should be. Its long ProfileConfig.Version is the version edited.",
            "$ref": "#/components/schemas/ProfileConfig"
          },
          "dryRun": {
            "type": "boolean",
            "description": "Run every check (authorize, apply, validate, compile) and return the diff, but persist nothing."
          },
          "force": {
            "type": "boolean",
            "description": "Do the change even though it cuts the caller's own way into the admin. When a command arrives through the\nadmin's internal listener, a change that deletes the admin host, disables it or removes its HTTPS is refused unless\nthis is set (the API's `?force=true`, a command's `force`); on any other surface it has no effect."
          },
          "message": {
            "type": [
              "null",
              "string"
            ],
            "description": "An optional note stored with the revision (a commit message). At most 1000 characters."
          }
        },
        "additionalProperties": false,
        "description": "Creates a profile (long ProfileConfig.Version 0) or replaces one."
      },
      "ConfigCommandUpsertRateLimitPolicyCommand": {
        "required": [
          "policy"
        ],
        "properties": {
          "command": {
            "enum": [
              "upsertRateLimitPolicy"
            ],
            "type": "string"
          },
          "policy": {
            "description": "The policy as it should be. Its long RateLimitPolicyConfig.Version is the version edited.",
            "$ref": "#/components/schemas/RateLimitPolicyConfig"
          },
          "dryRun": {
            "type": "boolean",
            "description": "Run every check (authorize, apply, validate, compile) and return the diff, but persist nothing."
          },
          "force": {
            "type": "boolean",
            "description": "Do the change even though it cuts the caller's own way into the admin. When a command arrives through the\nadmin's internal listener, a change that deletes the admin host, disables it or removes its HTTPS is refused unless\nthis is set (the API's `?force=true`, a command's `force`); on any other surface it has no effect."
          },
          "message": {
            "type": [
              "null",
              "string"
            ],
            "description": "An optional note stored with the revision (a commit message). At most 1000 characters."
          }
        },
        "additionalProperties": false,
        "description": "Creates a rate-limit policy (long RateLimitPolicyConfig.Version 0) or replaces one."
      },
      "ConfigCommandUpsertRuleSetCommand": {
        "required": [
          "ruleSet"
        ],
        "properties": {
          "command": {
            "enum": [
              "upsertRuleSet"
            ],
            "type": "string"
          },
          "ruleSet": {
            "description": "The set as it should be. Its long RuleSetConfig.Version is the version edited.",
            "$ref": "#/components/schemas/RuleSetConfig"
          },
          "dryRun": {
            "type": "boolean",
            "description": "Run every check (authorize, apply, validate, compile) and return the diff, but persist nothing."
          },
          "force": {
            "type": "boolean",
            "description": "Do the change even though it cuts the caller's own way into the admin. When a command arrives through the\nadmin's internal listener, a change that deletes the admin host, disables it or removes its HTTPS is refused unless\nthis is set (the API's `?force=true`, a command's `force`); on any other surface it has no effect."
          },
          "message": {
            "type": [
              "null",
              "string"
            ],
            "description": "An optional note stored with the revision (a commit message). At most 1000 characters."
          }
        },
        "additionalProperties": false,
        "description": "Creates a rule set (long RuleSetConfig.Version 0) or replaces one. A built-in preset can't be written."
      },
      "ConfigCommandUpsertUpstreamCommand": {
        "required": [
          "upstream"
        ],
        "properties": {
          "command": {
            "enum": [
              "upsertUpstream"
            ],
            "type": "string"
          },
          "upstream": {
            "description": "The upstream as it should be. Its long UpstreamConfig.Version is the version edited.",
            "$ref": "#/components/schemas/UpstreamConfig"
          },
          "dryRun": {
            "type": "boolean",
            "description": "Run every check (authorize, apply, validate, compile) and return the diff, but persist nothing."
          },
          "force": {
            "type": "boolean",
            "description": "Do the change even though it cuts the caller's own way into the admin. When a command arrives through the\nadmin's internal listener, a change that deletes the admin host, disables it or removes its HTTPS is refused unless\nthis is set (the API's `?force=true`, a command's `force`); on any other surface it has no effect."
          },
          "message": {
            "type": [
              "null",
              "string"
            ],
            "description": "An optional note stored with the revision (a commit message). At most 1000 characters."
          }
        },
        "additionalProperties": false,
        "description": "Creates an upstream (long UpstreamConfig.Version 0) or replaces one, destinations included."
      },
      "ConfigConflict": {
        "required": [
          "changedSinceBase"
        ],
        "type": "object",
        "properties": {
          "changedSinceBase": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ConfigChange"
            },
            "description": "Whole-document commands: what changed from the base revision to the latest (empty\n            when the base revision is unknown). Object commands: empty; IReadOnlyList&lt;ConfigChange&gt; ConfigCommandResult.Changes holds what the\n            command would change on top of the current config."
          },
          "currentRevision": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "The latest revision; null while there is none.",
                "$ref": "#/components/schemas/RevisionRef"
              }
            ]
          },
          "currentVersion": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "description": "Object commands: the object's current version. (An update or delete of an object that\n            no longer exists is ConfigCommandStatus.NotFound, not a conflict; a create of one that already exists\n            is a conflict with its version here.) Whole-document commands: null.",
            "format": "int64"
          }
        },
        "additionalProperties": false,
        "description": "What a conflict dialog needs: what the command was based on against what is current."
      },
      "ConfigDocument": {
        "required": [
          "apiVersion"
        ],
        "type": "object",
        "properties": {
          "apiVersion": {
            "type": "string",
            "description": "Always string ConfigDocument.V1 for now; required in JSON.",
            "default": "paduraksa.dev/v1"
          },
          "settings": {
            "$ref": "#/components/schemas/GlobalSettings"
          },
          "profiles": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/ProfileConfig"
            }
          },
          "upstreams": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/UpstreamConfig"
            }
          },
          "hosts": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/HostConfig"
            }
          },
          "rateLimitPolicies": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/RateLimitPolicyConfig"
            }
          },
          "accessLists": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/AccessListConfig"
            }
          },
          "ruleSets": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/RuleSetConfig"
            }
          },
          "authProviders": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/AuthProviderConfig"
            }
          }
        },
        "additionalProperties": false,
        "description": "The whole proxy config: global settings, profiles, upstreams, rate-limit policies, access lists and hosts with their routes. It is the\nsource of truth: each revision stores it as canonical JSON (ConfigJsonContext), and startup, rollback,\ndiff and YAML export all read it. It holds references only, never secrets."
      },
      "ConfigDraft": {
        "required": [
          "id",
          "version",
          "createdAt",
          "updatedAt",
          "expiresAt",
          "entries"
        ],
        "type": "object",
        "properties": {
          "id": {
            "description": "The draft.",
            "$ref": "#/components/schemas/DraftId"
          },
          "version": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Changes with every change to the draft; apply names the one it reviewed (`If-Match`).",
            "format": "int64"
          },
          "createdAt": {
            "type": "string",
            "description": "When it was started.",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "description": "When it was last changed; 30 days later (ExpiresAt) an untouched draft is dropped.",
            "format": "date-time"
          },
          "expiresAt": {
            "type": "string",
            "description": "When it is dropped if nobody touches it.",
            "format": "date-time"
          },
          "entries": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DraftEntry"
            },
            "description": "The entries in the order they were added, each with its state against the head."
          },
          "startedOn": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "The revision the draft was started on; null when there was none.",
                "$ref": "#/components/schemas/RevisionRef"
              }
            ]
          },
          "head": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "The latest revision now; null while there is none.",
                "$ref": "#/components/schemas/RevisionRef"
              }
            ]
          }
        },
        "additionalProperties": false,
        "description": "A user's open draft."
      },
      "ConfigError": {
        "required": [
          "path",
          "code",
          "message"
        ],
        "type": "object",
        "properties": {
          "path": {
            "type": "string",
            "description": "JSON pointer (RFC 6901) into the document, e.g. `/hosts/3/routes/0/path`; `\"\"` is the\n            whole document. Indices count in the document as validated: as the client sent it, before canonicalisation;\n            validation and compile errors both point at what the client wrote, and an object command's errors are relative to\n            the object it sent."
          },
          "code": {
            "type": "string",
            "description": "Stable, dotted identifier of the rule, e.g. `route.path.notCanonical`."
          },
          "message": {
            "type": "string",
            "description": "Human-readable explanation; wording may change."
          }
        },
        "additionalProperties": false,
        "description": "A validation or compile error in a config document."
      },
      "ConfigRevisionId": {
        "examples": [
          "rev_0A1b2C3d4E5f6G7h8I9j0K"
        ],
        "pattern": "^rev_[0-9A-Za-z]{22}$",
        "type": "string",
        "description": "A committed config revision."
      },
      "ConfigWarning": {
        "required": [
          "path",
          "code",
          "message"
        ],
        "type": "object",
        "properties": {
          "path": {
            "type": "string",
            "description": "JSON pointer into the document (`/hosts/3`), like string ConfigError.Path."
          },
          "code": {
            "type": "string",
            "description": "Stable, dotted identifier, e.g. `certificate.auto.noCoverage`."
          },
          "message": {
            "type": "string",
            "description": "Human-readable explanation; wording may change."
          }
        },
        "additionalProperties": false,
        "description": "Something about a config that compiles but probably isn't what the admin wants: a host without a certificate that\ncovers it, a binding to a certificate that expired. Surfaced next to the result (the dashboard's \"hosts without\ncoverage\"), never blocks a change on its own."
      },
      "ConnectionCounters": {
        "required": [
          "maxPerIp",
          "denyListRefused",
          "limitRefused",
          "uncounted",
          "counted"
        ],
        "type": "object",
        "properties": {
          "maxPerIp": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Open connections one client address may hold; 0 means the limit is off. Trusted proxies are exempt.",
            "format": "int32"
          },
          "denyListRefused": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Connections refused for being in the deny list.",
            "format": "int64"
          },
          "limitRefused": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Connections refused because their address already held the limit.",
            "format": "int64"
          },
          "uncounted": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Connections let through uncounted because the table had no entry for their address; 0 unless a flood from many addresses filled it.",
            "format": "int64"
          },
          "counted": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Connections the limit counts now.",
            "format": "int64"
          },
          "denyList": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "The access list whose deny addresses are refused before TLS; absent when none is named.",
                "$ref": "#/components/schemas/ConnectionDenyListRef"
              }
            ]
          }
        },
        "additionalProperties": false,
        "description": "The connection stage: what runs before the TLS handshake and what it has refused over the range, over TCP and QUIC."
      },
      "ConnectionDenyListRef": {
        "required": [
          "id",
          "name",
          "addressRanges"
        ],
        "type": "object",
        "properties": {
          "id": {
            "description": "The access list.",
            "$ref": "#/components/schemas/AccessListId"
          },
          "name": {
            "type": "string",
            "description": "Its name."
          },
          "addressRanges": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "The deny address ranges it covers, after merging overlaps.",
            "format": "int32"
          }
        },
        "additionalProperties": false,
        "description": "The connection deny list."
      },
      "ConnectionSettings": {
        "type": "object",
        "properties": {
          "maxConnectionsPerIp": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "description": "Open connections one client address may hold (an IPv6 /64 counts as one address), 0 to\n            100000; 0 turns the cap off. Null = the built-in 256. Over it a connection is closed at once, before TLS.",
            "format": "int32"
          },
          "denyListId": {
            "examples": [
              "acl_0A1b2C3d4E5f6G7h8I9j0K"
            ],
            "pattern": "^acl_[0-9A-Za-z]{22}$",
            "type": [
              "null",
              "string"
            ],
            "description": "The access list whose `ip.deny` addresses are refused before TLS, trusted proxies\n            included. Only its deny addresses apply here: its allow addresses, countries and users are request rules. Null = none."
          },
          "setsNothing": {
            "type": "boolean"
          }
        },
        "additionalProperties": false,
        "description": "What the proxy does with a connection before the TLS handshake (docs/design/hot-path.md \"A. Connection\"). Global only: a connection has no host yet.\nA visitor behind a trusted proxy (Cloudflare, a load balancer) shares the proxy's address here, so the proxy is exempt from\nthe cap and its visitors are judged by their real addresses later, at the gate."
      },
      "CreateAcmeAccountBody": {
        "required": [
          "directoryUrl",
          "termsOfServiceAgreed"
        ],
        "type": "object",
        "properties": {
          "directoryUrl": {
            "type": "string",
            "description": "The CA's directory URL (https)."
          },
          "termsOfServiceAgreed": {
            "type": "boolean",
            "description": "The operator agreed to the CA's terms; most CAs refuse an account without it."
          },
          "contacts": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "type": "string"
            },
            "description": "Contact URIs, e.g. `mailto:admin@example.com`; may be empty."
          },
          "algorithm": {
            "enum": [
              "ES256",
              "ES384",
              "RS256"
            ],
            "type": "string",
            "description": "The account key's algorithm; the vault generates the key.",
            "default": "ES256"
          },
          "eabKid": {
            "type": [
              "null",
              "string"
            ],
            "description": "The CA's binding key identifier, for CAs that require one; needs EabHmacKey."
          },
          "eabHmacKey": {
            "type": [
              "null",
              "string"
            ],
            "description": "The binding's HMAC key as the CA shows it (base64url); needs EabKid."
          },
          "label": {
            "type": [
              "null",
              "string"
            ],
            "description": "A display name."
          }
        },
        "additionalProperties": false,
        "description": "Registers an ACME account over the API. The external-account-binding key goes in here and is stored in the vault: it is never returned."
      },
      "CreateDnsProviderBody": {
        "required": [
          "kind",
          "label",
          "credential"
        ],
        "type": "object",
        "properties": {
          "kind": {
            "description": "Which service.",
            "$ref": "#/components/schemas/DnsProviderKind"
          },
          "label": {
            "type": "string",
            "description": "A display name (required)."
          },
          "credential": {
            "type": "string",
            "description": "The provider's API token."
          }
        },
        "additionalProperties": false,
        "description": "Adds a DNS provider over the API. The credential goes in here and is stored in the vault: it is never returned."
      },
      "CreateUserRequest": {
        "required": [
          "username",
          "displayName",
          "password",
          "roles"
        ],
        "type": "object",
        "properties": {
          "username": {
            "type": "string",
            "description": "3 to 64 characters: letters, digits and `. _ - @ +`. Unique without regard to case."
          },
          "displayName": {
            "type": [
              "null",
              "string"
            ],
            "description": "Shown in the UI; the username when null or empty."
          },
          "password": {
            "type": "string",
            "description": "The initial password (minimum length applies, no other rule)."
          },
          "roles": {
            "type": "array",
            "description": "At least one."
          }
        },
        "additionalProperties": false,
        "description": "Creates a user."
      },
      "CurrentCaller": {
        "required": [
          "user",
          "permissions",
          "authKind"
        ],
        "type": "object",
        "properties": {
          "user": {
            "description": "The user (for a token, the one it acts as).",
            "$ref": "#/components/schemas/UserSummary"
          },
          "permissions": {
            "type": "array",
            "description": "What the caller may do right now, in a fixed order: for a token, its scopes narrowed to what its user holds."
          },
          "authKind": {
            "description": "How the caller authenticated: AuthKind.Session or AuthKind.Token.",
            "$ref": "#/components/schemas/AuthKind"
          },
          "sessionId": {
            "examples": [
              "ses_0A1b2C3d4E5f6G7h8I9j0K"
            ],
            "pattern": "^ses_[0-9A-Za-z]{22}$",
            "type": [
              "null",
              "string"
            ],
            "description": "The session, for AuthKind.Session."
          },
          "tokenId": {
            "examples": [
              "tok_0A1b2C3d4E5f6G7h8I9j0K"
            ],
            "pattern": "^tok_[0-9A-Za-z]{22}$",
            "type": [
              "null",
              "string"
            ],
            "description": "The token, for AuthKind.Token."
          }
        },
        "additionalProperties": false,
        "description": "Who the admin API thinks the caller is."
      },
      "CurrentConfig": {
        "required": [
          "document"
        ],
        "type": "object",
        "properties": {
          "document": {
            "description": "Its canonical document, versions included.",
            "$ref": "#/components/schemas/ConfigDocument"
          },
          "revision": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "The latest revision; null while there is none (the empty config).",
                "$ref": "#/components/schemas/RevisionRef"
              }
            ]
          }
        },
        "additionalProperties": false,
        "description": "The current config, as an editor starts from it."
      },
      "DestinationConfig": {
        "required": [
          "id",
          "address"
        ],
        "type": "object",
        "properties": {
          "id": {
            "description": "Identity; also the YARP destination ID.",
            "$ref": "#/components/schemas/DestinationId"
          },
          "address": {
            "type": "string",
            "description": "Absolute `http`/`https` URL, e.g. `http://jellyfin:8096`, optionally with a\n            base path; no query, fragment or credentials."
          },
          "enabled": {
            "type": "boolean",
            "description": "False: kept in the document but not proxied to.",
            "default": true
          }
        },
        "additionalProperties": false,
        "description": "A destination of an upstream."
      },
      "DestinationHealthState": {
        "enum": [
          "unknown",
          "healthy",
          "unhealthy"
        ],
        "type": "string",
        "description": "One destination's health."
      },
      "DestinationHealthView": {
        "required": [
          "id",
          "address",
          "state"
        ],
        "type": "object",
        "properties": {
          "id": {
            "description": "The destination.",
            "$ref": "#/components/schemas/DestinationId"
          },
          "address": {
            "type": "string",
            "description": "Its configured address."
          },
          "state": {
            "description": "YARP's combined verdict, after the flap guard.",
            "$ref": "#/components/schemas/DestinationHealthState"
          },
          "reason": {
            "type": [
              "null",
              "string"
            ],
            "description": "Why it is unhealthy (\"active probe: HTTP 503\", \"passive check: too many failed requests\"); the last probe's result when healthy."
          },
          "since": {
            "type": [
              "null",
              "string"
            ],
            "description": "When it last changed state, if it has since the process started.",
            "format": "date-time"
          },
          "flapping": {
            "type": "boolean",
            "description": "It changed too often and its changes are not reported until it settles.",
            "default": false
          }
        },
        "additionalProperties": false,
        "description": "A destination as the proxy sees it."
      },
      "DestinationId": {
        "examples": [
          "dst_0A1b2C3d4E5f6G7h8I9j0K"
        ],
        "pattern": "^dst_[0-9A-Za-z]{22}$",
        "type": "string",
        "description": "A destination of an upstream; must stay stable for YARP health state to survive."
      },
      "DnsProviderId": {
        "examples": [
          "dnsp_0A1b2C3d4E5f6G7h8I9j0K"
        ],
        "pattern": "^dnsp_[0-9A-Za-z]{22}$",
        "type": "string",
        "description": "A configured DNS provider for DNS-01 challenges. Its credentials are a SecretId."
      },
      "DnsProviderKind": {
        "enum": [
          "cloudflare",
          "desec"
        ],
        "type": "string",
        "description": "The DNS services Paduraksa can drive for DNS-01. Persisted by name (ModelNames); adding a kind needs a migration for the column's check."
      },
      "DnsProviderSummary": {
        "required": [
          "id",
          "kind",
          "label",
          "createdAt",
          "updatedAt"
        ],
        "type": "object",
        "properties": {
          "id": {
            "$ref": "#/components/schemas/DnsProviderId"
          },
          "kind": {
            "$ref": "#/components/schemas/DnsProviderKind"
          },
          "label": {
            "type": "string"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "additionalProperties": false,
        "description": "A DNS provider as listed. No credential and no vault reference: the credential is named only by the internal row."
      },
      "DnsProviderTestOutcome": {
        "required": [
          "valid"
        ],
        "type": "object",
        "properties": {
          "valid": {
            "type": "boolean",
            "description": "The provider accepted the credential."
          },
          "message": {
            "type": [
              "null",
              "string"
            ],
            "description": "Why not, for an operator."
          }
        },
        "additionalProperties": false,
        "description": "What the API answers to \"Test credentials\": the verdict. (Whether the provider exists is the HTTP status.)"
      },
      "DraftConfig": {
        "required": [
          "document",
          "draft"
        ],
        "type": "object",
        "properties": {
          "document": {
            "description": "The head's document with each ready entry applied in order; versions are the head's, not the ones a commit would give.",
            "$ref": "#/components/schemas/ConfigDocument"
          },
          "draft": {
            "description": "The draft.",
            "$ref": "#/components/schemas/ConfigDraft"
          },
          "head": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "The latest revision; null while there is none.",
                "$ref": "#/components/schemas/RevisionRef"
              }
            ]
          }
        },
        "additionalProperties": false,
        "description": "The head with a draft's entries applied: what every page and picker of the dashboard reads while a draft is open."
      },
      "DraftEntry": {
        "required": [
          "number",
          "action",
          "target",
          "command",
          "addedAt"
        ],
        "type": "object",
        "properties": {
          "number": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "The entry's number in its draft: 1, 2, 3, ... in the order added, never reused within the draft.",
            "format": "int32"
          },
          "action": {
            "description": "What it does.",
            "$ref": "#/components/schemas/DraftEntryAction"
          },
          "target": {
            "type": "string",
            "description": "The object it is about, `hosts/host_...` or `settings`; one entry per object, the later edit replacing the earlier."
          },
          "command": {
            "description": "The command, as it will be replayed. It names the version it was made against (an upsert's object version, a delete's\n            `Version`). A password typed into an access list is not in it: the user carries the `credentialId` of a pending vault value.",
            "$ref": "#/components/schemas/ConfigCommand"
          },
          "addedAt": {
            "type": "string",
            "description": "When the entry was added or last replaced.",
            "format": "date-time"
          },
          "state": {
            "enum": [
              "ready",
              "changedElsewhere"
            ],
            "type": "string",
            "description": "Against the config as it is now: worked out on every read, never stored.",
            "default": "ready"
          },
          "reason": {
            "type": [
              "null",
              "string"
            ],
            "description": "Why the entry is DraftEntryState.ChangedElsewhere, in words: its own object moved, or it depends on an entry that is blocked."
          }
        },
        "additionalProperties": false,
        "description": "One change in a draft: a config object command, kept with the version of the object it was made against."
      },
      "DraftEntryAction": {
        "enum": [
          "create",
          "update",
          "delete"
        ],
        "type": "string",
        "description": "What a draft entry does to its object."
      },
      "DraftId": {
        "examples": [
          "drf_0A1b2C3d4E5f6G7h8I9j0K"
        ],
        "pattern": "^drf_[0-9A-Za-z]{22}$",
        "type": "string",
        "description": "A draft: one user's open change set across pages, applied as one revision (docs/design/dashboard.md \"Drafts (M10)\")."
      },
      "DraftOutcome": {
        "required": [
          "result"
        ],
        "type": "object",
        "properties": {
          "result": {
            "description": "The dry run over the ready entries (ConfigCommandStatus.DryRun, invalid, ...), or the commit.",
            "$ref": "#/components/schemas/ConfigCommandResult"
          },
          "draft": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "The draft after the call; null when it is gone (it was applied, whole).",
                "$ref": "#/components/schemas/ConfigDraft"
              }
            ]
          },
          "reviewToken": {
            "type": [
              "null",
              "string"
            ],
            "description": "A review that passed and could be applied: what apply must send back as `If-Match` to commit exactly this. Absent when the\n            draft has blocked entries (it applies whole or not at all) or the set doesn't pass."
          }
        },
        "additionalProperties": false,
        "description": "The outcome of a review or an apply."
      },
      "ErrorPageContent": {
        "type": "object",
        "properties": {
          "mode": {
            "description": "How the page is written. Default: ErrorPageMode.Message.",
            "$ref": "#/components/schemas/ErrorPageMode"
          },
          "heading": {
            "type": [
              "null",
              "string"
            ],
            "description": "ErrorPageMode.Message only: the heading, 1 to 120 characters on one line. It is also the page's title."
          },
          "text": {
            "type": [
              "null",
              "string"
            ],
            "description": "ErrorPageMode.Message only: the sentence under the heading, up to 500 characters on one line; may be empty."
          },
          "html": {
            "type": [
              "null",
              "string"
            ],
            "description": "ErrorPageMode.Html only: a complete document, up to 64 KiB of UTF-8. It is served with\n        `Content-Security-Policy: default-src 'none'; style-src 'unsafe-inline'; img-src data:; font-src data:; base-uri 'none';\n        form-action 'none'; frame-ancestors 'none'`, so inline style and `data:` images work and nothing runs or loads from elsewhere."
          }
        },
        "additionalProperties": false,
        "description": "One error page: a heading and a sentence rendered into Paduraksa's own layout, or the operator's whole document. The\ntext may use the variables `{status}`, `{statusText}`, `{requestId}`, `{host}` and `{cfRay}`\n(ErrorPageText); `{{` writes a literal brace."
      },
      "ErrorPageMode": {
        "enum": [
          "message",
          "html"
        ],
        "type": "string",
        "description": "How an operator's error page is written."
      },
      "ErrorPagePreview": {
        "required": [
          "html",
          "contentSecurityPolicy",
          "sampleRequest",
          "sampleHost",
          "sampleCfRay"
        ],
        "type": "object",
        "properties": {
          "html": {
            "type": "string",
            "description": "The page's document, which a client shows in a sandboxed frame without scripts."
          },
          "contentSecurityPolicy": {
            "type": "string",
            "description": "The `Content-Security-Policy` the proxy serves it with."
          },
          "sampleRequest": {
            "type": "string",
            "description": "The ID `{requestId}` was filled with."
          },
          "sampleHost": {
            "type": "string",
            "description": "The name `{host}` was filled with."
          },
          "sampleCfRay": {
            "type": "string",
            "description": "The ray `{cfRay}` was filled with."
          }
        },
        "additionalProperties": false,
        "description": "A page rendered the way the proxy serves it, with sample values (`{requestId}`, `{host}`, `{cfRay}`)."
      },
      "ErrorPagePreviewRequest": {
        "required": [
          "page"
        ],
        "type": "object",
        "properties": {
          "page": {
            "description": "The page, as it would be stored.",
            "$ref": "#/components/schemas/ErrorPageContent"
          },
          "status": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "The status it is served with, 100 to 599; its `{status}` and `{statusText}` say it. For a page of one of the\n            statuses that is the status itself, for the maintenance page the status the host answers maintenance with.",
            "format": "int32",
            "default": 503
          },
          "maintenance": {
            "type": "boolean",
            "description": "The page is a maintenance page, which a host may serve with another status than an error page's.",
            "default": false
          }
        },
        "additionalProperties": false,
        "description": "A page to preview: the page as written and the status it would be served with."
      },
      "ErrorPageSource": {
        "enum": [
          "builtIn",
          "globalDefaults",
          "profile",
          "host"
        ],
        "type": "string",
        "description": "Which layer of the settings chain an error page came from."
      },
      "ErrorPagesSettings": {
        "type": "object",
        "properties": {
          "403": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "403: an access list, rule set or Cloudflare check refused the request.",
                "$ref": "#/components/schemas/ErrorPageContent"
              }
            ]
          },
          "404": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "404: no route takes the path, or the host is unknown.",
                "$ref": "#/components/schemas/ErrorPageContent"
              }
            ]
          },
          "413": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "413: the request body is over the host's or route's limit.",
                "$ref": "#/components/schemas/ErrorPageContent"
              }
            ]
          },
          "414": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "414: the request target is over the host's limit.",
                "$ref": "#/components/schemas/ErrorPageContent"
              }
            ]
          },
          "421": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "421: the connection can't be used for the host (a client that coalesced it must retry on its own).",
                "$ref": "#/components/schemas/ErrorPageContent"
              }
            ]
          },
          "429": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "429: a rate limit, or a queue of password checks, turned the request away.",
                "$ref": "#/components/schemas/ErrorPageContent"
              }
            ]
          },
          "431": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "431: the request has more headers, or bigger ones, than the host takes.",
                "$ref": "#/components/schemas/ErrorPageContent"
              }
            ]
          },
          "502": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "502: the upstream, or a sign-in service, didn't answer properly.",
                "$ref": "#/components/schemas/ErrorPageContent"
              }
            ]
          },
          "503": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "503: no healthy destination, or a sign-in service is busy.",
                "$ref": "#/components/schemas/ErrorPageContent"
              }
            ]
          },
          "504": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "504: the upstream, or a sign-in service, took too long.",
                "$ref": "#/components/schemas/ErrorPageContent"
              }
            ]
          },
          "maintenance": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "The page a host in maintenance answers with (MaintenanceSettings).",
                "$ref": "#/components/schemas/ErrorPageContent"
              }
            ]
          },
          "replaceUpstream": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Answer with the page above when the upstream's own response is an error with a page here (the statuses of this\nrecord), decided from the status and content type when the response starts: the upstream's body is discarded unread. Never for\nan upgrade, an event stream or gRPC. Built-in default: UpstreamErrorReplacement.Off.",
                "$ref": "#/components/schemas/UpstreamErrorReplacement"
              }
            ]
          }
        },
        "additionalProperties": false,
        "description": "The pages a host answers with in place of the built-in ones, one entry per status the gate and the upstream can answer\nwith a page, and whether an upstream's own error bodies are replaced by them. Each entry inherits on its own and is\nreplaced whole by a layer that sets it (a heading from one layer and a document from another make no page). A status with\nno entry in any layer is served from the built-in page."
      },
      "ForwardAuthConfig": {
        "required": [
          "url"
        ],
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "description": "The endpoint, an absolute `http` or `https` URL with no credentials. For a preset the bare address\n            (`http://authelia:9091`) is enough: the preset's path is added when the URL has none."
          },
          "preset": {
            "enum": [
              "custom",
              "authentik",
              "authelia",
              "oAuth2Proxy"
            ],
            "type": [
              "null",
              "string"
            ],
            "description": "Which provider this is; null is ForwardAuthPreset.Custom."
          },
          "timeoutSeconds": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "description": "How long to wait for the answer; null = int ForwardAuthLimits.DefaultTimeoutSeconds. A timeout refuses the request (504).",
            "format": "int32"
          },
          "cacheSeconds": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "description": "How long an allow is remembered; null = int ForwardAuthLimits.DefaultCacheSeconds, 0 = never. A session the provider has\n            revoked still works for that long.",
            "format": "int32"
          },
          "cacheScope": {
            "enum": [
              "request",
              "host"
            ],
            "type": [
              "null",
              "string"
            ],
            "description": "What a remembered allow covers; null = the preset's. `host` reuses an allow for every path only when the provider named\n            someone (copied an identity header); an allow without one is remembered for its own method and path."
          },
          "requestHeaders": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "type": "string"
            },
            "description": "Client headers sent to the endpoint besides the standard ones (cookies, authorization, user agent, accept and the\n            `X-Forwarded-*` set); null = none."
          },
          "responseHeaders": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "type": "string"
            },
            "description": "The endpoint's response headers copied to the upstream when it allows a request, and removed from the client's own\n            request first. A trailing `*` makes a name a prefix. Null = the preset's; an empty list = none. `Authorization`, `Cookie` and\n            `Proxy-Authorization` can't be listed: the client's own are forwarded as they were."
          },
          "signIn": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Where a browser without a session is sent when the endpoint answers 401; null = the preset's. Without one the 401 is relayed as it is.",
                "$ref": "#/components/schemas/ForwardAuthSignInConfig"
              }
            ]
          }
        },
        "additionalProperties": false,
        "description": "A forward-auth endpoint. For each request the proxy asks it with a `GET` that carries the client's credentials and the original\nrequest in `X-Forwarded-Method`, `-Proto`, `-Host`, `-Uri` and `-For`: a 2xx lets the request through with the\nconfigured response headers copied upstream, a 401, 403 or redirect is relayed to the client, anything else (and a timeout) refuses.\nA field left null takes the preset's value (ForwardAuthPresets); ResolvedForwardAuth ForwardAuthPresets.Resolve(ForwardAuthConfig config) is the one\nplace that says which."
      },
      "ForwardAuthSignInConfig": {
        "required": [
          "url"
        ],
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "description": "Absolute, or a path on the protected host (`/oauth2/start`)."
          },
          "returnParameter": {
            "type": [
              "null",
              "string"
            ],
            "description": "The query parameter that carries the original URL (`rd`); null = `rd`."
          }
        },
        "additionalProperties": false,
        "description": "A sign-in page a browser is redirected to, with the address it came from in a query parameter."
      },
      "GlobalSettings": {
        "type": "object",
        "properties": {
          "version": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Concurrency version, incremented by the command pipeline on every change.",
            "format": "int64",
            "default": 0
          },
          "unknownHost": {
            "enum": [
              "notFound",
              "misdirectedRequest",
              "close"
            ],
            "type": "string",
            "description": "What a request for a name that no enabled host serves gets.",
            "default": "notFound"
          },
          "defaultCertificateId": {
            "examples": [
              "crt_0A1b2C3d4E5f6G7h8I9j0K"
            ],
            "pattern": "^crt_[0-9A-Za-z]{22}$",
            "type": [
              "null",
              "string"
            ],
            "description": "Certificate served when the SNI matches no host or is absent, and to hosts whose\n            binding resolves to no certificate of their own. Null: such handshakes are rejected."
          },
          "defaults": {
            "$ref": "#/components/schemas/HostSettings"
          },
          "trustedProxies": {
            "$ref": "#/components/schemas/TrustedProxySettings"
          },
          "connections": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Null when it sets nothing (bool ConnectionSettings.SetsNothing): canonical JSON writes no key, so a\n            stored revision keeps its bytes and its hash.",
                "$ref": "#/components/schemas/ConnectionSettings"
              }
            ]
          },
          "server": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Null when it sets nothing (bool ServerSettings.SetsNothing), for the same reason.",
                "$ref": "#/components/schemas/ServerSettings"
              }
            ]
          }
        },
        "additionalProperties": false,
        "description": "The global settings: one per document."
      },
      "HeaderRuleConfig": {
        "required": [
          "name"
        ],
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "The header's name: 1-64 token characters."
          },
          "operation": {
            "enum": [
              "set",
              "add",
              "remove"
            ],
            "type": "string",
            "description": "What to do with the header.",
            "default": "set"
          },
          "value": {
            "type": [
              "null",
              "string"
            ],
            "description": "The value HeaderOperation.Set and HeaderOperation.Add send: 1-4096 visible\n            ASCII characters, spaces or tabs, with no leading or trailing whitespace. Sent as written, with no placeholders. Absent for\n            HeaderOperation.Remove."
          }
        },
        "additionalProperties": false,
        "description": "One custom header rule."
      },
      "HeadersSettings": {
        "type": "object",
        "properties": {
          "request": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/HeaderRuleConfig"
            },
            "description": "Rules for the request the upstream receives, at most 32, one per header name; at most 32 also after the layers merge."
          },
          "response": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/HeaderRuleConfig"
            },
            "description": "Rules for the response the client receives, at most 32, one per header name; at most 32 also after the layers merge."
          }
        },
        "additionalProperties": false,
        "description": "Custom headers: rules for the request the upstream receives and for the response the client receives. The layers merge\nrule by rule, keyed on the header name (ignoring case) per direction: a layer's rule for a name replaces the rule a lower\nlayer has for it, and every other inherited rule stays. A route's rules go over its host's the same way."
      },
      "HealthChangeView": {
        "required": [
          "at",
          "summary"
        ],
        "type": "object",
        "properties": {
          "at": {
            "type": "string",
            "description": "When the change began.",
            "format": "date-time"
          },
          "summary": {
            "type": "string",
            "description": "What changed (\"Down\", \"Back up\", \"10.0.0.5:8096 down\")."
          },
          "reason": {
            "type": [
              "null",
              "string"
            ],
            "description": "Why, where YARP says."
          }
        },
        "additionalProperties": false,
        "description": "The most recent state change of an upstream or one of its destinations."
      },
      "HostConfig": {
        "required": [
          "id",
          "names"
        ],
        "type": "object",
        "properties": {
          "id": {
            "description": "Identity.",
            "$ref": "#/components/schemas/HostId"
          },
          "names": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Exact names, `*.parent` wildcards or IP literals; a set. Canonical form: normalised\n            (trimmed, lowercase, IDN → punycode, no trailing dot), sorted ordinally, no duplicates. Unique across enabled\n            hosts."
          },
          "version": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Concurrency version.",
            "format": "int64",
            "default": 0
          },
          "kind": {
            "enum": [
              "proxy",
              "redirect",
              "respond",
              "admin"
            ],
            "type": "string",
            "description": "What the host does with requests.",
            "default": "proxy"
          },
          "enabled": {
            "type": "boolean",
            "description": "False: validated, but serves nothing; its names may then repeat an enabled host's.",
            "default": true
          },
          "profileId": {
            "examples": [
              "prof_0A1b2C3d4E5f6G7h8I9j0K"
            ],
            "pattern": "^prof_[0-9A-Za-z]{22}$",
            "type": [
              "null",
              "string"
            ],
            "description": "The profile layer, between the global defaults and this host; null = none."
          },
          "redirect": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Redirect hosts only (required).",
                "$ref": "#/components/schemas/RedirectConfig"
              }
            ]
          },
          "respond": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Respond hosts only (required).",
                "$ref": "#/components/schemas/RespondConfig"
              }
            ]
          },
          "notes": {
            "type": [
              "null",
              "string"
            ],
            "description": "Free text."
          },
          "certificate": {
            "$ref": "#/components/schemas/CertificateBinding"
          },
          "settings": {
            "$ref": "#/components/schemas/HostSettings"
          },
          "routes": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/RouteConfig"
            }
          },
          "tags": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "type": "string"
            }
          }
        },
        "additionalProperties": false,
        "description": "A host: the names it answers for and what it does with requests."
      },
      "HostId": {
        "examples": [
          "host_0A1b2C3d4E5f6G7h8I9j0K"
        ],
        "pattern": "^host_[0-9A-Za-z]{22}$",
        "type": "string",
        "description": "A host."
      },
      "HostProtections": {
        "required": [
          "id",
          "name",
          "proofOfWork",
          "rateLimit",
          "robots",
          "accessList"
        ],
        "type": "object",
        "properties": {
          "id": {
            "description": "The host.",
            "$ref": "#/components/schemas/HostId"
          },
          "name": {
            "type": "string",
            "description": "Its first name."
          },
          "proofOfWork": {
            "description": "Whether visitors must solve a challenge.",
            "$ref": "#/components/schemas/ProofOfWorkProtection"
          },
          "rateLimit": {
            "description": "The rate-limit policy, if any.",
            "$ref": "#/components/schemas/RateLimitProtection"
          },
          "robots": {
            "description": "How the host answers robots.txt.",
            "$ref": "#/components/schemas/RobotsMode"
          },
          "accessList": {
            "description": "The access list that guards the host, and the routes that differ from it.",
            "$ref": "#/components/schemas/AccessListProtection"
          },
          "originLockdown": {
            "enum": [
              "off",
              "forbid",
              "drop"
            ],
            "type": "string",
            "description": "What the host does with a peer that isn't Cloudflare's; off unless the client-address mode is a Cloudflare one.",
            "default": "off"
          },
          "authenticatedOriginPulls": {
            "type": "boolean",
            "description": "Whether the host's handshake requires Cloudflare's client certificate (the same condition).",
            "default": false
          },
          "ruleSet": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "The rule set that applies to the host, and the routes that differ from it; absent when the response comes from an older server.",
                "$ref": "#/components/schemas/RuleSetProtection"
              }
            ]
          },
          "authProvider": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "The auth provider that asks who a request is, and the routes that differ from it; absent when the response comes from an older server.",
                "$ref": "#/components/schemas/AuthProviderProtection"
              }
            ]
          },
          "refused": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Requests to the host the gate refused over the range (any reason long RefusalReasons.Refused counts).",
            "format": "int64",
            "default": 0
          }
        },
        "additionalProperties": false,
        "description": "One enabled host's protections, from the live compiled config."
      },
      "HostSettings": {
        "type": "object",
        "properties": {
          "forceHttps": {
            "type": [
              "null",
              "boolean"
            ],
            "description": "Redirect plain-HTTP requests to HTTPS before anything else. Built-in default: off."
          },
          "tls": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Merged field by field with the layers below.",
                "$ref": "#/components/schemas/TlsSettings"
              }
            ]
          },
          "hsts": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Merged field by field with the layers below.",
                "$ref": "#/components/schemas/HstsSettings"
              }
            ]
          },
          "accessLog": {
            "type": [
              "null",
              "boolean"
            ],
            "description": "Write a line for every request of this host to the access log. Built-in default: on. A host that turns\nit off is still proxied, and its requests still carry a request ID; they just aren't logged (the drop counter and\nthe error log are unaffected)."
          },
          "requestIdPropagation": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Where the request ID travels (RequestIdPropagation? HostSettings.RequestIdPropagation). Built-in default: upstream and client, which is\nwhat correlates a request across the proxy's log, the app's log and an error page.",
                "$ref": "#/components/schemas/RequestIdPropagation"
              }
            ]
          },
          "serverHeader": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Merged field by field with the layers below. Built-in default: `Paduraksa`, with no version.",
                "$ref": "#/components/schemas/ServerHeaderSettings"
              }
            ]
          },
          "proofOfWork": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Merged field by field with the layers below. Built-in default: off.",
                "$ref": "#/components/schemas/ProofOfWorkSettings"
              }
            ]
          },
          "rateLimit": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Merged field by field with the layers below. Built-in default: none.",
                "$ref": "#/components/schemas/RateLimitSettings"
              }
            ]
          },
          "robots": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Merged field by field with the layers below. Built-in default: pass through to the upstream.",
                "$ref": "#/components/schemas/RobotsSettings"
              }
            ]
          },
          "cloudflare": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Merged field by field with the layers below. Built-in default: no lockdown, no Authenticated Origin Pulls.",
                "$ref": "#/components/schemas/CloudflareSettings"
              }
            ]
          },
          "accessList": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Merged field by field with the layers below. Built-in default: none.",
                "$ref": "#/components/schemas/AccessListSettings"
              }
            ]
          },
          "requestLimits": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Merged field by field with the layers below. Built-in default: 100 MiB of body, 100 header lines, 32 KiB of\n            headers, an 8 KiB request target.",
                "$ref": "#/components/schemas/RequestLimitsSettings"
              }
            ]
          },
          "ruleSet": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Merged field by field with the layers below. Built-in default: none.",
                "$ref": "#/components/schemas/RuleSetSettings"
              }
            ]
          },
          "authProvider": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Merged field by field with the layers below. Built-in default: none.",
                "$ref": "#/components/schemas/AuthProviderSettings"
              }
            ]
          },
          "errorPages": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Merged entry by entry with the layers below. Built-in default: the built-in pages, and an upstream's own error bodies untouched.",
                "$ref": "#/components/schemas/ErrorPagesSettings"
              }
            ]
          },
          "maintenance": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Merged field by field with the layers below. Built-in default: off.",
                "$ref": "#/components/schemas/MaintenanceSettings"
              }
            ]
          },
          "headers": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Merged rule by rule (keyed on the header name) with the layers below. Built-in default: none.",
                "$ref": "#/components/schemas/HeadersSettings"
              }
            ]
          },
          "compression": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Merged field by field with the layers below. Built-in default: off.",
                "$ref": "#/components/schemas/CompressionSettings"
              }
            ]
          }
        },
        "additionalProperties": false,
        "description": "One layer of host settings. Every field is nullable and null (absent in JSON) means\n<i>inherit</i>: the compiler resolves built-in defaults → global defaults → profile → host → route. Nested settings\n(TlsSettings? HostSettings.Tls, HstsSettings? HostSettings.Hsts, ServerHeaderSettings? HostSettings.ServerHeader, ProofOfWorkSettings? HostSettings.ProofOfWork, RateLimitSettings? HostSettings.RateLimit,\nRobotsSettings? HostSettings.Robots, CloudflareSettings? HostSettings.Cloudflare, AccessListSettings? HostSettings.AccessList, RequestLimitsSettings? HostSettings.RequestLimits, RuleSetSettings? HostSettings.RuleSet, AuthProviderSettings? HostSettings.AuthProvider, ErrorPagesSettings? HostSettings.ErrorPages, MaintenanceSettings? HostSettings.Maintenance, HeadersSettings? HostSettings.Headers, CompressionSettings? HostSettings.Compression) merge field by field."
      },
      "HostTraffic": {
        "required": [
          "id",
          "name",
          "requests",
          "errorRate",
          "refused",
          "p95Ms"
        ],
        "type": "object",
        "properties": {
          "id": {
            "description": "The host; HostId HostId.Empty is the requests no host owns (an unknown name).",
            "$ref": "#/components/schemas/HostId"
          },
          "name": {
            "type": "string",
            "description": "The host's first name in the live config, or its ID when it is gone."
          },
          "requests": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Requests answered over the range.",
            "format": "int64"
          },
          "errorRate": {
            "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?$",
            "type": [
              "number",
              "string"
            ],
            "description": "The share answered with a 5xx, 0 to 1.",
            "format": "double"
          },
          "refused": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Requests the gate refused.",
            "format": "int64"
          },
          "p95Ms": {
            "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?$",
            "type": [
              "number",
              "string"
            ],
            "description": "The 95th-percentile total time; over a longer range it is the mean of the minutes' percentiles, weighted by their requests.",
            "format": "double"
          }
        },
        "additionalProperties": false,
        "description": "A host's traffic over the range."
      },
      "HstsSettings": {
        "type": "object",
        "properties": {
          "enabled": {
            "type": [
              "null",
              "boolean"
            ],
            "description": "Send the header at all. Built-in default: off, so nothing is pinned on a browser's behalf until it is\n            turned on."
          },
          "maxAge": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "description": "Seconds a browser remembers the host as HTTPS-only (`max-age`), 0 to 63072000 (two years). Built-in\n            default: one year (31536000). 0 is valid, and tells a browser that already remembers the host to forget it.",
            "format": "int32"
          },
          "includeSubDomains": {
            "type": [
              "null",
              "boolean"
            ],
            "description": "Cover every subdomain too (`includeSubDomains`). Built-in default: off, because a subdomain that has\n            no certificate of its own stops working over plain HTTP. A browser begins to trust the setting when it sees it, and\n            keeps it for int? HstsSettings.MaxAge, so a wrong one is felt for as long as that."
          },
          "preload": {
            "type": [
              "null",
              "boolean"
            ],
            "description": "Ask the browser vendors' preload lists to ship the host as HTTPS-only (`preload`). Built-in default:\n            off. It needs bool? HstsSettings.IncludeSubDomains and a int? HstsSettings.MaxAge of at least a year, and it is hard to undo:\n            browsers that ship the list keep refusing plain HTTP even after the header stops."
          }
        },
        "additionalProperties": false,
        "description": "HTTP Strict Transport Security: the host's HTTPS responses tell a browser to keep the host on HTTPS for\nint? HstsSettings.MaxAge seconds, and with bool? HstsSettings.IncludeSubDomains every subdomain too. Each field inherits on its\nown. Paduraksa sends it only to HTTPS requests, and never for an IP literal or a loopback name."
      },
      "IssuanceOutcome": {
        "enum": [
          "issued",
          "failed",
          "inProgress",
          "invalid",
          "notFound",
          "unavailable",
          "forbidden"
        ],
        "type": "string",
        "description": "How an issuance, renewal or resume ended."
      },
      "IssuanceResult": {
        "required": [
          "outcome"
        ],
        "type": "object",
        "properties": {
          "outcome": {
            "description": "How it ended.",
            "$ref": "#/components/schemas/IssuanceOutcome"
          },
          "certificateId": {
            "examples": [
              "crt_0A1b2C3d4E5f6G7h8I9j0K"
            ],
            "pattern": "^crt_[0-9A-Za-z]{22}$",
            "type": [
              "null",
              "string"
            ],
            "description": "The certificate (allocated by a request); null when refused before one existed."
          },
          "certificate": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "The certificate's metadata after the attempt; null when there is none.",
                "$ref": "#/components/schemas/CertificateSummary"
              }
            ]
          },
          "message": {
            "type": [
              "null",
              "string"
            ],
            "description": "Why it failed (sanitised, no secrets)."
          },
          "errors": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/ConfigError"
            }
          }
        },
        "additionalProperties": false,
        "description": "The outcome of an issuance, renewal or resume."
      },
      "IssueApiTokenRequest": {
        "required": [
          "name",
          "scopes"
        ],
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "1 to 128 characters."
          },
          "scopes": {
            "type": "array",
            "description": "At least one, each a permission the user holds."
          },
          "expiresAt": {
            "type": [
              "null",
              "string"
            ],
            "description": "In the future, or null for a token that never expires.",
            "format": "date-time"
          }
        },
        "additionalProperties": false,
        "description": "Issues an API token for the signed-in user."
      },
      "IssuedApiToken": {
        "required": [
          "summary",
          "token"
        ],
        "type": "object",
        "properties": {
          "summary": {
            "description": "The token's public facts.",
            "$ref": "#/components/schemas/ApiTokenSummary"
          },
          "token": {
            "type": "string",
            "description": "The full `pdk_...` string to present as a Bearer token. Not recoverable later."
          }
        },
        "additionalProperties": false,
        "description": "A newly issued token: the only time its secret is shown."
      },
      "JwsAlgorithm": {
        "enum": [
          "ES256",
          "ES384",
          "RS256"
        ],
        "type": "string",
        "description": "The JWS signature algorithms an ACME account key can use (RFC 8555 §6.2 requires a public-key algorithm and\nforbids MACs and `none` for account keys). Persisted by the JOSE name (JwsAlgorithmNames), never by number."
      },
      "KeySpec": {
        "enum": [
          "ecdsa-p256",
          "ecdsa-p384",
          "rsa-2048",
          "rsa-3072",
          "rsa-4096"
        ],
        "type": "string",
        "description": "The kind of key the vault generates for a certificate. Persisted by its stable name (VaultNames), never\nby number. KeySpec.EcdsaP256 is the zero value and so the default."
      },
      "LatencyFigures": {
        "required": [
          "p50Ms",
          "p95Ms",
          "p99Ms"
        ],
        "type": "object",
        "properties": {
          "p50Ms": {
            "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?$",
            "type": [
              "number",
              "string"
            ],
            "format": "double"
          },
          "p95Ms": {
            "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?$",
            "type": [
              "number",
              "string"
            ],
            "format": "double"
          },
          "p99Ms": {
            "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?$",
            "type": [
              "number",
              "string"
            ],
            "format": "double"
          }
        },
        "additionalProperties": false,
        "description": "Latency percentiles in milliseconds; all 0 when nothing was timed."
      },
      "LocalCaLeafRequest": {
        "required": [
          "names"
        ],
        "type": "object",
        "properties": {
          "names": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "DNS names, `*.` wildcards and IP addresses; at least one."
          },
          "keySpec": {
            "enum": [
              "ecdsa-p256",
              "ecdsa-p384",
              "rsa-2048",
              "rsa-3072",
              "rsa-4096"
            ],
            "type": "string",
            "description": "The kind of key the vault generates.",
            "default": "ecdsa-p256"
          },
          "validity": {
            "pattern": "^-?(\\d+\\.)?\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,7})?$",
            "type": [
              "null",
              "string"
            ],
            "description": "How long it is valid from now (the vault caps it at the root's remaining validity); null for the default (90 days)."
          },
          "label": {
            "type": [
              "null",
              "string"
            ],
            "description": "A display name."
          }
        },
        "additionalProperties": false,
        "description": "A new leaf certificate signed by the local CA."
      },
      "LocalCaReissueResult": {
        "required": [
          "reissued",
          "failures"
        ],
        "type": "object",
        "properties": {
          "reissued": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "How many were issued again from the current root.",
            "format": "int32"
          },
          "failures": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ConfigError"
            },
            "description": "The ones that were not, with why (a name the new root's constraints don't permit, for one)."
          }
        },
        "additionalProperties": false,
        "description": "What re-issuing the certificates a retiring root signed did."
      },
      "LocalCaRequest": {
        "type": "object",
        "properties": {
          "keySpec": {
            "enum": [
              "ecdsa-p256",
              "ecdsa-p384",
              "rsa-2048",
              "rsa-3072",
              "rsa-4096"
            ],
            "type": "string",
            "description": "The kind of key the vault generates for the root.",
            "default": "ecdsa-p256"
          },
          "validity": {
            "pattern": "^-?(\\d+\\.)?\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,7})?$",
            "type": [
              "null",
              "string"
            ],
            "description": "How long the root is valid from now; null for the default (ten years)."
          },
          "label": {
            "type": [
              "null",
              "string"
            ],
            "description": "A display name."
          },
          "permittedDomains": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "type": "string"
            },
            "description": "DNS domains to limit the root to: a name equal to one or under it. A device that trusts the root then accepts nothing for any\n            other name. With no domains and no addresses the root is unlimited, which is the default."
          },
          "permittedAddresses": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "type": "string"
            },
            "description": "IP addresses or CIDR networks to limit the root to."
          }
        },
        "additionalProperties": false,
        "description": "Creates the local CA root, if there is none. There is one local CA: asking again returns the existing root."
      },
      "LocalCaRotateRequest": {
        "type": "object",
        "properties": {
          "keySpec": {
            "enum": [
              "ecdsa-p256",
              "ecdsa-p384",
              "rsa-2048",
              "rsa-3072",
              "rsa-4096"
            ],
            "type": "string",
            "description": "The kind of key the vault generates for the new root.",
            "default": "ecdsa-p256"
          },
          "validity": {
            "pattern": "^-?(\\d+\\.)?\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,7})?$",
            "type": [
              "null",
              "string"
            ],
            "description": "How long the new root is valid from now; null for the default (ten years)."
          },
          "permittedDomains": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "type": "string"
            },
            "description": "As on LocalCaRequest; the new root's own list, so add every domain it should issue for. None (and no addresses) makes the new root unlimited."
          },
          "permittedAddresses": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "type": "string"
            },
            "description": "As on LocalCaRequest."
          }
        },
        "additionalProperties": false,
        "description": "Replaces the local CA root with a new one. Devices must trust the new root before what it signs works for them; what the old root signed\nkeeps working (and the old root stays downloadable) until it is retired."
      },
      "LocalCaStatus": {
        "type": "object",
        "properties": {
          "root": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "The root that signs; null before there is one.",
                "$ref": "#/components/schemas/CertificateSummary"
              }
            ]
          },
          "permittedNames": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "type": "string"
            }
          },
          "retiring": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/RetiringLocalCaRoot"
            }
          },
          "suggestedDomains": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "type": "string"
            }
          },
          "suggestedAddresses": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "type": "string"
            }
          }
        },
        "additionalProperties": false,
        "description": "The local CA as it stands: the root that signs, what it is limited to, and the roots a rotation left behind."
      },
      "LogFileSummary": {
        "required": [
          "name",
          "log",
          "size",
          "lastModified",
          "compressed"
        ],
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "The file's name inside the logs directory, e.g. `error.log` or `access-20261109T120000Z.log.gz`."
          },
          "log": {
            "type": "string",
            "description": "The log it belongs to: `access` or `error`."
          },
          "size": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Its size in bytes.",
            "format": "int64"
          },
          "lastModified": {
            "type": "string",
            "description": "When it was last written, in UTC.",
            "format": "date-time"
          },
          "compressed": {
            "type": "boolean",
            "description": "True for a rotated file that has been gzipped."
          }
        },
        "additionalProperties": false,
        "description": "One file in the logs directory: its name, which log it belongs to, its size, and when it was last written.\nServed by the API's log list and shown by the dashboard's log page."
      },
      "LogFileTail": {
        "required": [
          "name",
          "log",
          "lines",
          "truncated",
          "fileSize"
        ],
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "The file these lines come from."
          },
          "log": {
            "type": "string",
            "description": "The log it belongs to: `access` or `error`."
          },
          "lines": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The lines, oldest first, each at most 4096 characters."
          },
          "truncated": {
            "type": "boolean",
            "description": "True when the file (or the part that was read) held more than Lines carries."
          },
          "fileSize": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "The file's size in bytes.",
            "format": "int64"
          }
        },
        "additionalProperties": false,
        "description": "The last lines of a log file, read without loading the whole file: at most 1000 lines out of the last 1 MiB (a rotated\nfile is decompressed up to a cap first), optionally only those matching a filter."
      },
      "LogTailEntry": {
        "required": [
          "sequence",
          "time",
          "totalMs"
        ],
        "type": "object",
        "properties": {
          "sequence": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Position in the stream since the process started. A jump between two is what the reader skipped.",
            "format": "int64"
          },
          "time": {
            "type": "string",
            "description": "When the request ended, UTC.",
            "format": "date-time"
          },
          "totalMs": {
            "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?$",
            "type": [
              "number",
              "string"
            ],
            "description": "Request start to the end of the response, in milliseconds.",
            "format": "double"
          },
          "clientAddress": {
            "type": [
              "null",
              "string"
            ],
            "description": "The resolved client address (behind a trusted proxy, the one its header carried)."
          },
          "host": {
            "type": [
              "null",
              "string"
            ],
            "description": "The `Host` as received, port included."
          },
          "matchedHost": {
            "type": [
              "null",
              "string"
            ],
            "description": "The configured host's first name, or absent when no host owned the request (a scanner, a typo, an address)."
          },
          "method": {
            "type": [
              "null",
              "string"
            ],
            "description": "The request method."
          },
          "path": {
            "type": [
              "null",
              "string"
            ],
            "description": "The decoded path."
          },
          "query": {
            "type": [
              "null",
              "string"
            ],
            "description": "The query, `?` included, with sensitive parameters as `name=REDACTED`; absent when there is none or the file leaves it out."
          },
          "protocol": {
            "type": [
              "null",
              "string"
            ],
            "description": "`HTTP/1.1`, `HTTP/2` or `HTTP/3`."
          },
          "status": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "description": "The status sent, or absent when the gate dropped the connection without answering.",
            "format": "int32"
          },
          "responseBytes": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "description": "Response body bytes written to the client.",
            "format": "int64"
          },
          "requestBytes": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "description": "Request body bytes read from the client.",
            "format": "int64"
          },
          "upstreamMs": {
            "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?$",
            "type": [
              "null",
              "number",
              "string"
            ],
            "description": "The forward to the upstream, in milliseconds; absent when nothing was forwarded.",
            "format": "double"
          },
          "upstream": {
            "type": [
              "null",
              "string"
            ],
            "description": "The destination the forwarder chose."
          },
          "requestIdentifier": {
            "type": [
              "null",
              "string"
            ],
            "description": "The `X-Request-Id` of the request: the one a trusted proxy sent, or Paduraksa's own."
          },
          "cloudflareRay": {
            "type": [
              "null",
              "string"
            ],
            "description": "`CF-Ray`, only when the request came through a trusted Cloudflare edge or tunnel."
          },
          "referer": {
            "type": [
              "null",
              "string"
            ],
            "description": "The `Referer` header."
          },
          "userAgent": {
            "type": [
              "null",
              "string"
            ],
            "description": "The `User-Agent` header."
          },
          "timing": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Where the request's time went, stage by stage.",
                "$ref": "#/components/schemas/LogTailTiming"
              }
            ]
          }
        },
        "additionalProperties": false,
        "description": "One request in the live access-log tail, as `access.log` would show it: the same caps, the same escaping of control\ncharacters (`\\xNN`) and the same redaction of sensitive query parameters. A request of a host whose access log is off is\nnever here."
      },
      "LogTailTiming": {
        "type": "object",
        "properties": {
          "connectionRequest": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "description": "The request's index on its connection, from 1.",
            "format": "int32"
          },
          "tlsHandshakeMs": {
            "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?$",
            "type": [
              "null",
              "number",
              "string"
            ],
            "description": "The connection's TLS handshake, on its first request only.",
            "format": "double"
          },
          "tlsReused": {
            "type": "boolean",
            "description": "A later request on a TLS connection: its handshake was paid by the first.",
            "default": false
          },
          "requestBodyMs": {
            "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?$",
            "type": [
              "null",
              "number",
              "string"
            ],
            "description": "From the first to the last request body byte read; 0 when there was no body.",
            "format": "double"
          },
          "gateMs": {
            "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?$",
            "type": [
              "null",
              "number",
              "string"
            ],
            "description": "The top of the pipeline to the gate's hand-off (to the forward, or to its own answer): the WAF, rate limits, access lists, auth and rules.",
            "format": "double"
          },
          "upstreamConnectMs": {
            "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?$",
            "type": [
              "null",
              "number",
              "string"
            ],
            "description": "The upstream connect (DNS, TCP and TLS); 0 on a pooled connection.",
            "format": "double"
          },
          "upstreamWaitMs": {
            "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?$",
            "type": [
              "null",
              "number",
              "string"
            ],
            "description": "Waiting for the upstream (TTFB, DevTools' \"Waiting for server response\"): from when the request was\n            sent upstream (its body's last byte, or its headers without a body) to the upstream's response headers, or to the failure of a\n            forward that got none. 0 when the upstream answered before the body was all sent.",
            "format": "double"
          },
          "responseSendMs": {
            "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?$",
            "type": [
              "null",
              "number",
              "string"
            ],
            "description": "The response's headers to its last byte written to the client.",
            "format": "double"
          },
          "requestBodyStartMs": {
            "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?$",
            "type": [
              "null",
              "number",
              "string"
            ],
            "description": "When the first request body byte was read; absent without a body.",
            "format": "double"
          },
          "upstreamConnectStartMs": {
            "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?$",
            "type": [
              "null",
              "number",
              "string"
            ],
            "description": "When the upstream connect started; absent on a pooled connection.",
            "format": "double"
          },
          "upstreamWaitStartMs": {
            "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?$",
            "type": [
              "null",
              "number",
              "string"
            ],
            "description": "When the wait started.",
            "format": "double"
          },
          "responseSendStartMs": {
            "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?$",
            "type": [
              "null",
              "number",
              "string"
            ],
            "description": "When the response's headers went out.",
            "format": "double"
          }
        },
        "additionalProperties": false,
        "description": "One request's time by stage, in milliseconds, laid out as Chrome DevTools' Timing tab: each stage has a length and a start, in\nmilliseconds from the request's start (the gate always starts at 0). A stage that didn't happen, or wasn't measured, is absent. The\nstages overlap (the body is forwarded as it arrives; a streamed answer is sent while the upstream still writes it), so they don't\nadd up to the total, and the time between them is time no stage accounts for."
      },
      "LoginRequest": {
        "required": [
          "username",
          "password"
        ],
        "type": "object",
        "properties": {
          "username": {
            "type": "string",
            "description": "The sign-in name (case does not matter)."
          },
          "password": {
            "type": "string",
            "description": "The password."
          }
        },
        "additionalProperties": false,
        "description": "The first step of signing in."
      },
      "MaintenanceSettings": {
        "type": "object",
        "properties": {
          "enabled": {
            "type": [
              "null",
              "boolean"
            ],
            "description": "Serve the maintenance page. Built-in default: off. Not applied to an admin host (the config warns), so a layer\n            that turns it on for every host can't lock the dashboard out."
          },
          "status": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "description": "The status of the answer: 503, 200, 500, 502, 504, 403, 404 or 410. Built-in default: 503.",
            "format": "int32"
          },
          "retryAfterSeconds": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "description": "Seconds a client is told to wait (`Retry-After`), 0 to 604800 (a week); 0 sends none. Built-in default: 0.",
            "format": "int32"
          },
          "bypassAccessListId": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "A named access list whose allowed addresses and countries reach the host as usual: a request the list's allow rules admit\nskips the maintenance page. Only the address rules count (a password is never asked for here, and a list with no allow rule\nbypasses nobody). Built-in default: none.",
                "$ref": "#/components/schemas/AccessListId"
              }
            ]
          }
        },
        "additionalProperties": false,
        "description": "A host that is down on purpose. While bool? MaintenanceSettings.Enabled, every request but the bypassed ones is answered with the\nmaintenance page and int? MaintenanceSettings.Status instead of reaching the upstream: after the host's access list, rules and HTTPS\nredirect, so a refused client is still refused and plain HTTP still redirects, and after the managed `robots.txt`.\nEach field inherits on its own."
      },
      "MetricsFigures": {
        "required": [
          "requestsPerSecond",
          "errorRate",
          "refusedPerSecond",
          "bytesPerSecond",
          "bytesInPerSecond",
          "total",
          "upstream"
        ],
        "type": "object",
        "properties": {
          "requestsPerSecond": {
            "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?$",
            "type": [
              "number",
              "string"
            ],
            "description": "Requests answered a second, the connections the gate dropped included.",
            "format": "double"
          },
          "errorRate": {
            "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?$",
            "type": [
              "number",
              "string"
            ],
            "description": "The share of requests answered with a 5xx, 0 to 1.",
            "format": "double"
          },
          "refusedPerSecond": {
            "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?$",
            "type": [
              "number",
              "string"
            ],
            "description": "Requests the gate refused by a policy or a limit, a second.",
            "format": "double"
          },
          "bytesPerSecond": {
            "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?$",
            "type": [
              "number",
              "string"
            ],
            "description": "Response body bytes written to clients a second.",
            "format": "double"
          },
          "bytesInPerSecond": {
            "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?$",
            "type": [
              "number",
              "string"
            ],
            "description": "Request body bytes read from clients a second.",
            "format": "double"
          },
          "total": {
            "description": "Time from the request's first byte to the end of the response.",
            "$ref": "#/components/schemas/LatencyFigures"
          },
          "upstream": {
            "description": "Time spent forwarding and waiting for the upstream; the difference to Total is the proxy's own.",
            "$ref": "#/components/schemas/LatencyFigures"
          }
        },
        "additionalProperties": false,
        "description": "The proxy's pace over the last half minute."
      },
      "MetricsOverview": {
        "required": [
          "at",
          "range",
          "bucketWidth",
          "now",
          "series",
          "hosts",
          "upstreams"
        ],
        "type": "object",
        "properties": {
          "at": {
            "type": "string",
            "description": "When it was read.",
            "format": "date-time"
          },
          "range": {
            "description": "How far back Series and the tables reach.",
            "$ref": "#/components/schemas/MetricsRange"
          },
          "bucketWidth": {
            "pattern": "^-?(\\d+\\.)?\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,7})?$",
            "type": "string",
            "description": "The usual width of one Series step; see int MetricsSeriesPoint.WidthSeconds for each step's own."
          },
          "now": {
            "description": "The last half minute.",
            "$ref": "#/components/schemas/MetricsFigures"
          },
          "series": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MetricsSeriesPoint"
            },
            "description": "The chart, oldest first, from the first step the server observed to the last: every step in between is there, and one the server\nwas not running for has int MetricsSeriesPoint.CoveredSeconds 0 (no data), so downtime is a gap and not a line through it."
          },
          "hosts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/HostTraffic"
            },
            "description": "The busiest hosts over the range, most requests first."
          },
          "upstreams": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/UpstreamTraffic"
            },
            "description": "The upstreams that were forwarded to over the range, most errors first."
          }
        },
        "additionalProperties": false,
        "description": "The overview page's data: the pace now, a chart over the range, the busiest hosts and the upstreams' errors. Read from the\nsampler's memory (and the last hour of rollups it started from) for the hour and from the persisted one-minute rollups for longer."
      },
      "MetricsRange": {
        "enum": [
          "hour",
          "day",
          "week",
          "month"
        ],
        "type": "string",
        "description": "How far back the overview looks. The hour comes from the sampler's memory, the rest from the one-minute rollups."
      },
      "MetricsSeriesPoint": {
        "required": [
          "start",
          "requestsPerSecond",
          "errorsPerSecond",
          "refusedPerSecond",
          "total",
          "upstream",
          "widthSeconds",
          "coveredSeconds"
        ],
        "type": "object",
        "properties": {
          "start": {
            "type": "string",
            "description": "The step's first second.",
            "format": "date-time"
          },
          "requestsPerSecond": {
            "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?$",
            "type": [
              "number",
              "string"
            ],
            "description": "Requests answered a second over the seconds that were observed, not the step's whole width.",
            "format": "double"
          },
          "errorsPerSecond": {
            "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?$",
            "type": [
              "number",
              "string"
            ],
            "description": "5xx answers a second, over the observed seconds.",
            "format": "double"
          },
          "refusedPerSecond": {
            "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?$",
            "type": [
              "number",
              "string"
            ],
            "description": "Requests the gate refused by a policy or a limit, a second over the observed seconds.",
            "format": "double"
          },
          "total": {
            "description": "Latency percentiles of the requests answered in the step.",
            "$ref": "#/components/schemas/LatencyFigures"
          },
          "upstream": {
            "description": "Upstream time percentiles of the requests forwarded in the step.",
            "$ref": "#/components/schemas/LatencyFigures"
          },
          "widthSeconds": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "How long the step is. Usually TimeSpan MetricsOverview.BucketWidth; the hour's steps from before the server started are a minute wide (the one it started in is cut short where the ten-second steps begin).",
            "format": "int32"
          },
          "coveredSeconds": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "How many of those seconds the server was running to count them; less than the width for a step the server started or stopped in, and 0 for no data.",
            "format": "int32"
          }
        },
        "additionalProperties": false,
        "description": "One step of the chart: the figures over the WidthSeconds starting at Start. A step whose\nCoveredSeconds is 0 has no data: Paduraksa was not running (every figure is 0, and a chart should break its\nline there rather than draw a zero). A step that was covered and saw no requests has a rate of 0 and no latency (0)."
      },
      "NotificationChannelBody": {
        "required": [
          "kind",
          "label",
          "events"
        ],
        "type": "object",
        "properties": {
          "kind": {
            "description": "Which service. Fixed once the channel exists.",
            "$ref": "#/components/schemas/NotificationChannelKind"
          },
          "label": {
            "type": "string",
            "description": "A display name (required)."
          },
          "events": {
            "type": "array",
            "description": "What the channel is sent; at least one."
          },
          "enabled": {
            "type": "boolean",
            "description": "False pauses the channel without losing its settings.",
            "default": true
          },
          "serverUrl": {
            "type": [
              "null",
              "string"
            ],
            "description": "ntfy and Gotify: the server's base URL (http or https)."
          },
          "topic": {
            "type": [
              "null",
              "string"
            ],
            "description": "ntfy: the topic to publish to."
          },
          "smtpHost": {
            "type": [
              "null",
              "string"
            ],
            "description": "Email: the SMTP server."
          },
          "smtpPort": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "description": "Email: its port (587 for STARTTLS, 465 for implicit TLS).",
            "format": "int32"
          },
          "smtpSecurity": {
            "enum": [
              "starttls",
              "implicit-tls",
              "none"
            ],
            "type": [
              "null",
              "string"
            ],
            "description": "Email: how the connection is secured; StartTls when omitted."
          },
          "smtpUsername": {
            "type": [
              "null",
              "string"
            ],
            "description": "Email: the login, when the server asks for one; needs the password as `Secret`."
          },
          "mailFrom": {
            "type": [
              "null",
              "string"
            ],
            "description": "Email: the sender address."
          },
          "mailTo": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "type": "string"
            },
            "description": "Email: the recipients (at least one)."
          },
          "secret": {
            "type": [
              "null",
              "string"
            ],
            "description": "The webhook URL (Discord, webhook), the application token (Gotify), the access token (ntfy, optional) or the SMTP password. On a replacement, null keeps the stored one."
          },
          "signingSecret": {
            "type": [
              "null",
              "string"
            ],
            "description": "Webhook: the key that signs each body (HMAC-SHA-256); null on a replacement keeps the stored one, an empty string removes it."
          }
        },
        "additionalProperties": false,
        "description": "A notification channel, to add or to replace. Which fields apply depends on NotificationChannelKind NotificationChannelBody.Kind: ntfy needs `ServerUrl` and\n`Topic`; Gotify `ServerUrl`; Discord and webhook only their secret (the URL); email `SmtpHost`, `SmtpPort`,\n`SmtpSecurity`, `MailFrom` and `MailTo` (and `SmtpUsername` with a password as the secret). The secrets go into the vault and are never returned."
      },
      "NotificationChannelFigures": {
        "required": [
          "queued",
          "sent",
          "failed"
        ],
        "type": "object",
        "properties": {
          "queued": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Deliveries waiting to be sent or retried.",
            "format": "int32"
          },
          "sent": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Deliveries the service accepted.",
            "format": "int32"
          },
          "failed": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Deliveries given up on, or not sent because their event was too old when the process started. The success rate is `sent / (sent + failed)`; dropped ones (the channel was deleted or paused) are in neither.",
            "format": "int32"
          }
        },
        "additionalProperties": false,
        "description": "How a channel's deliveries are going: what waits now, and the outcomes of the last seven days (by when their event happened)."
      },
      "NotificationChannelId": {
        "examples": [
          "ntf_0A1b2C3d4E5f6G7h8I9j0K"
        ],
        "pattern": "^ntf_[0-9A-Za-z]{22}$",
        "type": "string",
        "description": "A notification channel: one place events are sent to (an ntfy topic, a Discord webhook, a mailbox). Its secrets are vault SecretIds."
      },
      "NotificationChannelKind": {
        "enum": [
          "ntfy",
          "gotify",
          "discord",
          "webhook",
          "email"
        ],
        "type": "string",
        "description": "The services a notification can be sent through. Persisted by name (ModelNames); adding a kind needs a migration for the column's check."
      },
      "NotificationChannelSummary": {
        "required": [
          "id",
          "kind",
          "label",
          "enabled",
          "events",
          "mailTo",
          "hasSecret",
          "hasSigningSecret",
          "createdAt",
          "updatedAt"
        ],
        "type": "object",
        "properties": {
          "id": {
            "description": "The channel.",
            "$ref": "#/components/schemas/NotificationChannelId"
          },
          "kind": {
            "description": "Which service.",
            "$ref": "#/components/schemas/NotificationChannelKind"
          },
          "label": {
            "type": "string",
            "description": "The display name."
          },
          "enabled": {
            "type": "boolean",
            "description": "False when the channel is paused."
          },
          "events": {
            "type": "array",
            "description": "What the channel is sent."
          },
          "mailTo": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Email: the recipients."
          },
          "hasSecret": {
            "type": "boolean",
            "description": "A secret is stored."
          },
          "hasSigningSecret": {
            "type": "boolean",
            "description": "A webhook signing key is stored."
          },
          "createdAt": {
            "type": "string",
            "description": "When the channel was added.",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "description": "When its settings or secret last changed.",
            "format": "date-time"
          },
          "serverUrl": {
            "type": [
              "null",
              "string"
            ],
            "description": "ntfy and Gotify: the server's base URL."
          },
          "topic": {
            "type": [
              "null",
              "string"
            ],
            "description": "ntfy: the topic."
          },
          "smtpHost": {
            "type": [
              "null",
              "string"
            ],
            "description": "Email: the SMTP server."
          },
          "smtpPort": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "description": "Email: its port.",
            "format": "int32"
          },
          "smtpSecurity": {
            "enum": [
              "starttls",
              "implicit-tls",
              "none"
            ],
            "type": [
              "null",
              "string"
            ],
            "description": "Email: how the connection is secured."
          },
          "smtpUsername": {
            "type": [
              "null",
              "string"
            ],
            "description": "Email: the login, if any."
          },
          "mailFrom": {
            "type": [
              "null",
              "string"
            ],
            "description": "Email: the sender address."
          },
          "lastSentAt": {
            "type": [
              "null",
              "string"
            ],
            "description": "The last time a notification was delivered.",
            "format": "date-time"
          },
          "lastErrorAt": {
            "type": [
              "null",
              "string"
            ],
            "description": "The last time a delivery failed (after its retries).",
            "format": "date-time"
          },
          "lastError": {
            "type": [
              "null",
              "string"
            ],
            "description": "What went wrong then, in a sentence; cleared by the next delivery."
          },
          "delivery": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "How its deliveries are going; null in the answer to a change, which does not read them.",
                "$ref": "#/components/schemas/NotificationChannelFigures"
              }
            ]
          }
        },
        "additionalProperties": false,
        "description": "A channel as listed: its settings and how delivery is going. No secret and no vault reference, only whether one is set."
      },
      "NotificationDeliveryId": {
        "examples": [
          "ndl_0A1b2C3d4E5"
        ],
        "pattern": "^ndl_[0-9A-Za-z]{11}$",
        "type": "string",
        "description": "One event's delivery to one channel. Int64Ordered, like its event."
      },
      "NotificationDeliveryState": {
        "enum": [
          "pending",
          "sent",
          "failed",
          "expired",
          "dropped"
        ],
        "type": "string",
        "description": "Where one delivery of an event to one channel stands. Persisted by name (ModelNames). NotificationDeliveryState.Pending is the only state that\nis not final: a retry puts a final one back to it."
      },
      "NotificationDeliverySummary": {
        "required": [
          "id",
          "channelId",
          "state",
          "attempts"
        ],
        "type": "object",
        "properties": {
          "id": {
            "description": "The delivery.",
            "$ref": "#/components/schemas/NotificationDeliveryId"
          },
          "channelId": {
            "description": "The channel it went (or goes) to.",
            "$ref": "#/components/schemas/NotificationChannelId"
          },
          "state": {
            "description": "Pending (waiting, or between retries), sent, failed (given up), expired (the event was too old when the process started) or dropped (the channel was deleted or paused).",
            "$ref": "#/components/schemas/NotificationDeliveryState"
          },
          "attempts": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "How many times a send was tried.",
            "format": "int32"
          },
          "channelLabel": {
            "type": [
              "null",
              "string"
            ],
            "description": "The channel's display name; absent when the channel has been deleted (the history stays)."
          },
          "channelKind": {
            "enum": [
              "ntfy",
              "gotify",
              "discord",
              "webhook",
              "email"
            ],
            "type": [
              "null",
              "string"
            ],
            "description": "The channel's service; absent when it has been deleted."
          },
          "nextAttemptAt": {
            "type": [
              "null",
              "string"
            ],
            "description": "When the next try is due; only for a pending delivery.",
            "format": "date-time"
          },
          "lastAttemptAt": {
            "type": [
              "null",
              "string"
            ],
            "description": "When it was last tried or dropped.",
            "format": "date-time"
          },
          "sentAt": {
            "type": [
              "null",
              "string"
            ],
            "description": "When the service accepted it.",
            "format": "date-time"
          },
          "lastError": {
            "type": [
              "null",
              "string"
            ],
            "description": "What went wrong the last time, in the fixed, secret-free wording the senders use."
          }
        },
        "additionalProperties": false,
        "description": "One event's delivery to one channel, as the history shows it."
      },
      "NotificationEventId": {
        "examples": [
          "nev_0A1b2C3d4E5"
        ],
        "pattern": "^nev_[0-9A-Za-z]{11}$",
        "type": "string",
        "description": "A notification event as the outbox stores it: what happened, once, however many channels it was addressed to. Int64Ordered: admin-only,\n            append-only and high-volume."
      },
      "NotificationEventKind": {
        "enum": [
          "upstream.down",
          "upstream.up",
          "destination.down",
          "destination.up",
          "certificate.expiring",
          "acme.renewal_failed",
          "acme.renewal_recovered",
          "signin.lockout",
          "signin.new_address",
          "test"
        ],
        "type": "string",
        "description": "The fixed catalogue of what a channel can subscribe to. Persisted by name (ModelNames), so a name is never reused;\nNotificationEventKind.Test is sent by hand and is not a subscription."
      },
      "NotificationHistoryEvent": {
        "required": [
          "id",
          "kind",
          "title",
          "body",
          "occurredAt",
          "createdAt",
          "deliveries"
        ],
        "type": "object",
        "properties": {
          "id": {
            "description": "The event; webhooks receive it as `eventId` (the digest's own ID when several events went out as one message).",
            "$ref": "#/components/schemas/NotificationEventId"
          },
          "kind": {
            "description": "Which event of the catalogue.",
            "$ref": "#/components/schemas/NotificationEventKind"
          },
          "title": {
            "type": "string",
            "description": "One line: what happened to which object."
          },
          "body": {
            "type": "string",
            "description": "What, since when, and what to do."
          },
          "occurredAt": {
            "type": "string",
            "description": "When it happened.",
            "format": "date-time"
          },
          "createdAt": {
            "type": "string",
            "description": "When it was published.",
            "format": "date-time"
          },
          "deliveries": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/NotificationDeliverySummary"
            },
            "description": "Its deliveries, in the order the channels were addressed."
          },
          "subject": {
            "type": [
              "null",
              "string"
            ],
            "description": "The object it is about (an upstream, destination, certificate or user ID); absent when it is about nothing in particular."
          },
          "linkPath": {
            "type": [
              "null",
              "string"
            ],
            "description": "The dashboard page that shows it (`/upstreams`)."
          }
        },
        "additionalProperties": false,
        "description": "An event that was published, with what happened to it on each channel. An event nothing was subscribed to has no deliveries."
      },
      "NotificationRetryOutcome": {
        "required": [
          "retried"
        ],
        "type": "object",
        "properties": {
          "retried": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "The deliveries put back to pending.",
            "format": "int32"
          }
        },
        "additionalProperties": false,
        "description": "The answer of a retry: how many deliveries are pending again; each is sent within seconds."
      },
      "NotificationTestOutcome": {
        "required": [
          "delivered"
        ],
        "type": "object",
        "properties": {
          "delivered": {
            "type": "boolean",
            "description": "The service accepted the message."
          },
          "error": {
            "type": [
              "null",
              "string"
            ],
            "description": "What went wrong, in a sentence a person can act on; never a URL or a token."
          }
        },
        "additionalProperties": false,
        "description": "The answer of \"send test\": whether the message was delivered, now, without retries."
      },
      "OriginLockdownMode": {
        "enum": [
          "off",
          "forbid",
          "drop"
        ],
        "type": [
          "null",
          "string"
        ]
      },
      "PageOfAccessListConfig": {
        "required": [
          "items"
        ],
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AccessListConfig"
            },
            "description": "The items, in the order the endpoint documents (newest first for history, ascending by ID for the rest)."
          },
          "nextCursor": {
            "type": [
              "null",
              "string"
            ],
            "description": "An opaque cursor to pass as `cursor` for the next page; null on the last page. Always written, so a client reads one shape whichever context wrote it."
          }
        },
        "additionalProperties": false,
        "description": "One page of a cursor-paginated list."
      },
      "PageOfAcmeAccountSummary": {
        "required": [
          "items"
        ],
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AcmeAccountSummary"
            },
            "description": "The items, in the order the endpoint documents (newest first for history, ascending by ID for the rest)."
          },
          "nextCursor": {
            "type": [
              "null",
              "string"
            ],
            "description": "An opaque cursor to pass as `cursor` for the next page; null on the last page. Always written, so a client reads one shape whichever context wrote it."
          }
        },
        "additionalProperties": false,
        "description": "One page of a cursor-paginated list."
      },
      "PageOfAcmeOrderSummary": {
        "required": [
          "items"
        ],
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AcmeOrderSummary"
            },
            "description": "The items, in the order the endpoint documents (newest first for history, ascending by ID for the rest)."
          },
          "nextCursor": {
            "type": [
              "null",
              "string"
            ],
            "description": "An opaque cursor to pass as `cursor` for the next page; null on the last page. Always written, so a client reads one shape whichever context wrote it."
          }
        },
        "additionalProperties": false,
        "description": "One page of a cursor-paginated list."
      },
      "PageOfApiTokenSummary": {
        "required": [
          "items"
        ],
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ApiTokenSummary"
            },
            "description": "The items, in the order the endpoint documents (newest first for history, ascending by ID for the rest)."
          },
          "nextCursor": {
            "type": [
              "null",
              "string"
            ],
            "description": "An opaque cursor to pass as `cursor` for the next page; null on the last page. Always written, so a client reads one shape whichever context wrote it."
          }
        },
        "additionalProperties": false,
        "description": "One page of a cursor-paginated list."
      },
      "PageOfAuditEventSummary": {
        "required": [
          "items"
        ],
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AuditEventSummary"
            },
            "description": "The items, in the order the endpoint documents (newest first for history, ascending by ID for the rest)."
          },
          "nextCursor": {
            "type": [
              "null",
              "string"
            ],
            "description": "An opaque cursor to pass as `cursor` for the next page; null on the last page. Always written, so a client reads one shape whichever context wrote it."
          }
        },
        "additionalProperties": false,
        "description": "One page of a cursor-paginated list."
      },
      "PageOfAuthProviderConfig": {
        "required": [
          "items"
        ],
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AuthProviderConfig"
            },
            "description": "The items, in the order the endpoint documents (newest first for history, ascending by ID for the rest)."
          },
          "nextCursor": {
            "type": [
              "null",
              "string"
            ],
            "description": "An opaque cursor to pass as `cursor` for the next page; null on the last page. Always written, so a client reads one shape whichever context wrote it."
          }
        },
        "additionalProperties": false,
        "description": "One page of a cursor-paginated list."
      },
      "PageOfCertificateSummary": {
        "required": [
          "items"
        ],
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CertificateSummary"
            },
            "description": "The items, in the order the endpoint documents (newest first for history, ascending by ID for the rest)."
          },
          "nextCursor": {
            "type": [
              "null",
              "string"
            ],
            "description": "An opaque cursor to pass as `cursor` for the next page; null on the last page. Always written, so a client reads one shape whichever context wrote it."
          }
        },
        "additionalProperties": false,
        "description": "One page of a cursor-paginated list."
      },
      "PageOfConfigDraft": {
        "required": [
          "items"
        ],
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ConfigDraft"
            },
            "description": "The items, in the order the endpoint documents (newest first for history, ascending by ID for the rest)."
          },
          "nextCursor": {
            "type": [
              "null",
              "string"
            ],
            "description": "An opaque cursor to pass as `cursor` for the next page; null on the last page. Always written, so a client reads one shape whichever context wrote it."
          }
        },
        "additionalProperties": false,
        "description": "One page of a cursor-paginated list."
      },
      "PageOfDnsProviderSummary": {
        "required": [
          "items"
        ],
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DnsProviderSummary"
            },
            "description": "The items, in the order the endpoint documents (newest first for history, ascending by ID for the rest)."
          },
          "nextCursor": {
            "type": [
              "null",
              "string"
            ],
            "description": "An opaque cursor to pass as `cursor` for the next page; null on the last page. Always written, so a client reads one shape whichever context wrote it."
          }
        },
        "additionalProperties": false,
        "description": "One page of a cursor-paginated list."
      },
      "PageOfHostConfig": {
        "required": [
          "items"
        ],
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/HostConfig"
            },
            "description": "The items, in the order the endpoint documents (newest first for history, ascending by ID for the rest)."
          },
          "nextCursor": {
            "type": [
              "null",
              "string"
            ],
            "description": "An opaque cursor to pass as `cursor` for the next page; null on the last page. Always written, so a client reads one shape whichever context wrote it."
          }
        },
        "additionalProperties": false,
        "description": "One page of a cursor-paginated list."
      },
      "PageOfNotificationChannelSummary": {
        "required": [
          "items"
        ],
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/NotificationChannelSummary"
            },
            "description": "The items, in the order the endpoint documents (newest first for history, ascending by ID for the rest)."
          },
          "nextCursor": {
            "type": [
              "null",
              "string"
            ],
            "description": "An opaque cursor to pass as `cursor` for the next page; null on the last page. Always written, so a client reads one shape whichever context wrote it."
          }
        },
        "additionalProperties": false,
        "description": "One page of a cursor-paginated list."
      },
      "PageOfNotificationHistoryEvent": {
        "required": [
          "items"
        ],
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/NotificationHistoryEvent"
            },
            "description": "The items, in the order the endpoint documents (newest first for history, ascending by ID for the rest)."
          },
          "nextCursor": {
            "type": [
              "null",
              "string"
            ],
            "description": "An opaque cursor to pass as `cursor` for the next page; null on the last page. Always written, so a client reads one shape whichever context wrote it."
          }
        },
        "additionalProperties": false,
        "description": "One page of a cursor-paginated list."
      },
      "PageOfProfileConfig": {
        "required": [
          "items"
        ],
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ProfileConfig"
            },
            "description": "The items, in the order the endpoint documents (newest first for history, ascending by ID for the rest)."
          },
          "nextCursor": {
            "type": [
              "null",
              "string"
            ],
            "description": "An opaque cursor to pass as `cursor` for the next page; null on the last page. Always written, so a client reads one shape whichever context wrote it."
          }
        },
        "additionalProperties": false,
        "description": "One page of a cursor-paginated list."
      },
      "PageOfRateLimitPolicyConfig": {
        "required": [
          "items"
        ],
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RateLimitPolicyConfig"
            },
            "description": "The items, in the order the endpoint documents (newest first for history, ascending by ID for the rest)."
          },
          "nextCursor": {
            "type": [
              "null",
              "string"
            ],
            "description": "An opaque cursor to pass as `cursor` for the next page; null on the last page. Always written, so a client reads one shape whichever context wrote it."
          }
        },
        "additionalProperties": false,
        "description": "One page of a cursor-paginated list."
      },
      "PageOfRevisionSummary": {
        "required": [
          "items"
        ],
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RevisionSummary"
            },
            "description": "The items, in the order the endpoint documents (newest first for history, ascending by ID for the rest)."
          },
          "nextCursor": {
            "type": [
              "null",
              "string"
            ],
            "description": "An opaque cursor to pass as `cursor` for the next page; null on the last page. Always written, so a client reads one shape whichever context wrote it."
          }
        },
        "additionalProperties": false,
        "description": "One page of a cursor-paginated list."
      },
      "PageOfRouteConfig": {
        "required": [
          "items"
        ],
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RouteConfig"
            },
            "description": "The items, in the order the endpoint documents (newest first for history, ascending by ID for the rest)."
          },
          "nextCursor": {
            "type": [
              "null",
              "string"
            ],
            "description": "An opaque cursor to pass as `cursor` for the next page; null on the last page. Always written, so a client reads one shape whichever context wrote it."
          }
        },
        "additionalProperties": false,
        "description": "One page of a cursor-paginated list."
      },
      "PageOfRuleSetConfig": {
        "required": [
          "items"
        ],
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RuleSetConfig"
            },
            "description": "The items, in the order the endpoint documents (newest first for history, ascending by ID for the rest)."
          },
          "nextCursor": {
            "type": [
              "null",
              "string"
            ],
            "description": "An opaque cursor to pass as `cursor` for the next page; null on the last page. Always written, so a client reads one shape whichever context wrote it."
          }
        },
        "additionalProperties": false,
        "description": "One page of a cursor-paginated list."
      },
      "PageOfSessionSummary": {
        "required": [
          "items"
        ],
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SessionSummary"
            },
            "description": "The items, in the order the endpoint documents (newest first for history, ascending by ID for the rest)."
          },
          "nextCursor": {
            "type": [
              "null",
              "string"
            ],
            "description": "An opaque cursor to pass as `cursor` for the next page; null on the last page. Always written, so a client reads one shape whichever context wrote it."
          }
        },
        "additionalProperties": false,
        "description": "One page of a cursor-paginated list."
      },
      "PageOfUpstreamConfig": {
        "required": [
          "items"
        ],
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/UpstreamConfig"
            },
            "description": "The items, in the order the endpoint documents (newest first for history, ascending by ID for the rest)."
          },
          "nextCursor": {
            "type": [
              "null",
              "string"
            ],
            "description": "An opaque cursor to pass as `cursor` for the next page; null on the last page. Always written, so a client reads one shape whichever context wrote it."
          }
        },
        "additionalProperties": false,
        "description": "One page of a cursor-paginated list."
      },
      "PageOfUserSummary": {
        "required": [
          "items"
        ],
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/UserSummary"
            },
            "description": "The items, in the order the endpoint documents (newest first for history, ascending by ID for the rest)."
          },
          "nextCursor": {
            "type": [
              "null",
              "string"
            ],
            "description": "An opaque cursor to pass as `cursor` for the next page; null on the last page. Always written, so a client reads one shape whichever context wrote it."
          }
        },
        "additionalProperties": false,
        "description": "One page of a cursor-paginated list."
      },
      "PassiveHealthConfig": {
        "type": "object",
        "properties": {
          "reactivationPeriod": {
            "pattern": "^-?(\\d+\\.)?\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,7})?$",
            "type": [
              "null",
              "string"
            ],
            "description": "How long an unhealthy destination stays out; null = YARP's default."
          }
        },
        "additionalProperties": false,
        "description": "Passive health: a destination with too many transport failures is taken out for a while."
      },
      "PasswordConfirmation": {
        "required": [
          "password"
        ],
        "type": "object",
        "properties": {
          "password": {
            "type": "string",
            "description": "The current password."
          }
        },
        "additionalProperties": false,
        "description": "Proves the person at the keyboard knows their password, for a sensitive change to their own sign-in."
      },
      "PathRewriteConfig": {
        "type": "object",
        "properties": {
          "stripPrefix": {
            "type": "boolean",
            "description": "Take the route's path off the front. A request that spells the prefix differently from the\n            route (encoded, with `//`, `;` or dot segments) can't be stripped without guessing, and gets 404. Not on a route\n            whose path is `/`.",
            "default": false
          },
          "addPrefix": {
            "type": [
              "null",
              "string"
            ],
            "description": "Put this in front of what is forwarded: starts with `/`, no trailing `/`, at most 256\n            characters of a URL path (letters, digits, `-._~!$&amp;'()*+,;=:@/` and percent escapes), with no empty or dot\n            segments. Sent as written."
          },
          "rewriteLocation": {
            "type": [
              "null",
              "boolean"
            ],
            "description": "With StripPrefix: give an upstream's `Location` the client's prefix back\n            (`/login` becomes `/app/login`; one that already starts with `/app` is left alone). Null is on; turn it off for an\n            upstream that redirects to other routes of the host, which the mapping would put under this one. Only with a strip."
          }
        },
        "additionalProperties": false,
        "description": "The path a route forwards: the client's, with the route's own path taken off the front, a fixed prefix put on, or both\n(`/app/x` becomes `/x`, or `/api/x`). It works on the path as the client sent it, without decoding or\nre-encoding it, and the query is untouched. With bool PathRewriteConfig.StripPrefix the upstream is told the stripped prefix in\n`X-Forwarded-Prefix`, and a `Location` it answers with that points into its own paths gets the client's\nprefix back."
      },
      "ProfileConfig": {
        "required": [
          "id",
          "name"
        ],
        "type": "object",
        "properties": {
          "id": {
            "description": "Identity.",
            "$ref": "#/components/schemas/ProfileId"
          },
          "name": {
            "type": "string",
            "description": "Human label, unique ignoring case."
          },
          "version": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Concurrency version.",
            "format": "int64",
            "default": 0
          },
          "notes": {
            "type": [
              "null",
              "string"
            ],
            "description": "Free text."
          },
          "settings": {
            "$ref": "#/components/schemas/HostSettings"
          }
        },
        "additionalProperties": false,
        "description": "A named preset of host settings, e.g. \"LAN only\"; a host names at most one."
      },
      "ProfileId": {
        "examples": [
          "prof_0A1b2C3d4E5f6G7h8I9j0K"
        ],
        "pattern": "^prof_[0-9A-Za-z]{22}$",
        "type": "string",
        "description": "A named preset of host-settings overrides."
      },
      "ProofOfWorkCounters": {
        "required": [
          "challengesServed",
          "solved",
          "verifyRejected",
          "blockedNonBrowser",
          "tokensRejected"
        ],
        "type": "object",
        "properties": {
          "challengesServed": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Challenge pages sent to browsers without a valid token.",
            "format": "int64"
          },
          "solved": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Challenges answered with a cookie.",
            "format": "int64"
          },
          "verifyRejected": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Answers to the verify endpoint that were refused: a bad challenge or solution.",
            "format": "int64"
          },
          "blockedNonBrowser": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Requests refused with 403 for having no token and not coming from a browser.",
            "format": "int64"
          },
          "tokensRejected": {
            "description": "Cookies that were not accepted, by reason.",
            "$ref": "#/components/schemas/TokenRejections"
          }
        },
        "additionalProperties": false,
        "description": "The proof-of-work gate's counters."
      },
      "ProofOfWorkProtection": {
        "required": [
          "enabled",
          "difficulty",
          "routeOverrides"
        ],
        "type": "object",
        "properties": {
          "enabled": {
            "type": "boolean",
            "description": "The host's own switch."
          },
          "difficulty": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Leading zero bits a solution needs (a route can't change it).",
            "format": "int32"
          },
          "routeOverrides": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Routes whose switch differs from the host's: exempt when the host is on, protected when it is off.",
            "format": "int32"
          }
        },
        "additionalProperties": false,
        "description": "A host's proof-of-work setting."
      },
      "ProofOfWorkSettings": {
        "type": "object",
        "properties": {
          "enabled": {
            "type": [
              "null",
              "boolean"
            ],
            "description": "Challenge visitors. Built-in default: off."
          },
          "difficulty": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "description": "Leading zero bits of SHA-256 a solution needs, 8 to 28. Built-in default: 16, well under a second on an\n            ordinary laptop.",
            "format": "int32"
          },
          "tokenLifetimeHours": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "description": "How long the cookie lasts, in hours, 1 to 720. Built-in default: 168 (seven days).",
            "format": "int32"
          }
        },
        "additionalProperties": false,
        "description": "The browser check: a visitor without a valid token gets a small page that solves a SHA-256 proof of work, then a\nsigned, expiring cookie bound to this host and client address lets it through. Each field inherits on its own."
      },
      "PublishOutcome": {
        "required": [
          "status",
          "generation",
          "errors",
          "revision"
        ],
        "type": "object",
        "properties": {
          "status": {
            "description": "The verdict.",
            "$ref": "#/components/schemas/PublishOutcomeStatus"
          },
          "generation": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "The live snapshot generation, when PublishOutcomeStatus.Published.",
            "format": "int64"
          },
          "errors": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Why it failed, if it did."
          },
          "revision": {
            "description": "The revision whose publish this is: the committed one, or the newer one that superseded it.",
            "$ref": "#/components/schemas/ConfigRevisionId"
          },
          "succeeded": {
            "type": "boolean"
          }
        },
        "additionalProperties": false,
        "description": "How the snapshot publish after a commit went. The publisher only publishes the latest snapshot:\nwhen a newer revision is queued before this one went live, this one is superseded, and its outcome is that of the\npublish that carries it (Revision then names the newer revision)."
      },
      "PublishOutcomeStatus": {
        "enum": [
          "published",
          "pending",
          "invalid",
          "rejected",
          "timedOut",
          "failed"
        ],
        "type": "string",
        "description": "The publisher's verdict on a committed revision's snapshot."
      },
      "RateLimitCounters": {
        "required": [
          "allowed",
          "limited"
        ],
        "type": "object",
        "properties": {
          "allowed": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Requests a policy let through.",
            "format": "int64"
          },
          "limited": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Requests a policy refused with 429.",
            "format": "int64"
          }
        },
        "additionalProperties": false,
        "description": "The rate limiter's counters."
      },
      "RateLimitOverride": {
        "required": [
          "routes"
        ],
        "type": "object",
        "properties": {
          "routes": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "How many routes.",
            "format": "int32"
          },
          "policy": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "The policy those routes apply; absent when they are not limited.",
                "$ref": "#/components/schemas/RateLimitPolicyRef"
              }
            ]
          }
        },
        "additionalProperties": false,
        "description": "Routes that apply another rate-limit policy, or none."
      },
      "RateLimitPolicyConfig": {
        "required": [
          "id",
          "name"
        ],
        "type": "object",
        "properties": {
          "id": {
            "description": "Identity; the first half of a bucket's stable slot identity.",
            "$ref": "#/components/schemas/RateLimitPolicyId"
          },
          "name": {
            "type": "string",
            "description": "Human label, unique ignoring case."
          },
          "version": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Concurrency version.",
            "format": "int64",
            "default": 0
          },
          "requests": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Whole requests per Per, at least 1. The token rate is this over the\n            period's seconds.",
            "format": "int32",
            "default": 60
          },
          "per": {
            "enum": [
              "second",
              "minute",
              "hour"
            ],
            "type": "string",
            "description": "The period the rate is measured over.",
            "default": "minute"
          },
          "burst": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "The bucket's size in tokens, at least 1: how many requests may arrive at once before the rate\n            has to earn more.",
            "format": "int32",
            "default": 60
          },
          "key": {
            "enum": [
              "clientIp",
              "clientIpAndPath",
              "header"
            ],
            "type": "string",
            "description": "What one bucket belongs to: a client IP, an IP and path, or a header value.",
            "default": "clientIp"
          },
          "header": {
            "type": [
              "null",
              "string"
            ],
            "description": "With RateLimitKey.Header: the request header whose value is the partition key.\n            Required exactly then, and a valid header name (a token)."
          },
          "notes": {
            "type": [
              "null",
              "string"
            ],
            "description": "Free text."
          }
        },
        "additionalProperties": false,
        "description": "A named token bucket that hosts and routes apply: int RateLimitPolicyConfig.Requests per RateLimitPeriod RateLimitPolicyConfig.Per seconds, with a\nbucket of int RateLimitPolicyConfig.Burst tokens, keyed the way RateLimitKey RateLimitPolicyConfig.Key says. It is a top-level object, so two hosts\ncan name one policy and still keep their own buckets (docs/design/config.md \"State that survives a publish\")."
      },
      "RateLimitPolicyId": {
        "examples": [
          "rlp_0A1b2C3d4E5f6G7h8I9j0K"
        ],
        "pattern": "^rlp_[0-9A-Za-z]{22}$",
        "type": "string",
        "description": "A rate-limit policy: one token bucket's rate, burst and partition key, which hosts and routes name."
      },
      "RateLimitPolicyRef": {
        "required": [
          "id",
          "name"
        ],
        "type": "object",
        "properties": {
          "id": {
            "description": "The policy.",
            "$ref": "#/components/schemas/RateLimitPolicyId"
          },
          "name": {
            "type": "string",
            "description": "Its name."
          }
        },
        "additionalProperties": false,
        "description": "A named rate-limit policy."
      },
      "RateLimitProtection": {
        "required": [
          "overrides"
        ],
        "type": "object",
        "properties": {
          "overrides": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RateLimitOverride"
            },
            "description": "The routes that differ from the host's policy, grouped by what they apply instead."
          },
          "policy": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "The policy the host applies; absent when it names none.",
                "$ref": "#/components/schemas/RateLimitPolicyRef"
              }
            ]
          }
        },
        "additionalProperties": false,
        "description": "A host's rate-limit setting."
      },
      "RateLimitSettings": {
        "type": "object",
        "properties": {
          "enabled": {
            "type": [
              "null",
              "boolean"
            ],
            "description": "Apply the resolved policy. Built-in default: on. Off turns an inherited policy off."
          },
          "policyId": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "The policy to apply; null = inherit. With bool? RateLimitSettings.Enabled off the reference is kept but inert, so\n            turning the switch back on needs no second lookup.",
                "$ref": "#/components/schemas/RateLimitPolicyId"
              }
            ]
          }
        },
        "additionalProperties": false,
        "description": "Which rate-limit policy a layer applies. RateLimitPolicyId? RateLimitSettings.PolicyId names a policy the document declares; it inherits\nfield by field, so a host can turn an inherited policy off without forgetting which one it was, and a route can\ninherit its host's, name another, or turn it off. A layer that names a policy without stating\nbool? RateLimitSettings.Enabled leaves the switch to the built-in default, which is on: naming a policy is the whole job."
      },
      "RecoveryCodes": {
        "required": [
          "codes"
        ],
        "type": "object",
        "properties": {
          "codes": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Single-use codes, e.g. `abcd-efgh-jkmn-pqrs`."
          }
        },
        "additionalProperties": false,
        "description": "Recovery codes, shown once: only their hashes are kept."
      },
      "RedirectConfig": {
        "required": [
          "target"
        ],
        "type": "object",
        "properties": {
          "target": {
            "type": "string",
            "description": "Absolute `http`/`https` URL in printable ASCII (punycode host, percent-encoded path);\n            it becomes the `Location` header."
          },
          "statusCode": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "301, 302, 303, 307 or 308.",
            "format": "int32",
            "default": 301
          },
          "preservePath": {
            "type": "boolean",
            "description": "Append the request's path and query to Target (which then has no\n            query or fragment).",
            "default": true
          }
        },
        "additionalProperties": false,
        "description": "A Redirect host's target."
      },
      "RefusalReasons": {
        "required": [
          "accessList",
          "ruleSet",
          "rateLimit",
          "proofOfWork",
          "signIn",
          "requestLimits",
          "connection",
          "other",
          "unknown",
          "noRoute",
          "unavailable",
          "dropped",
          "unknownHost"
        ],
        "type": "object",
        "properties": {
          "accessList": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "An access list refused them by address, country or password.",
            "format": "int64"
          },
          "ruleSet": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "A rule set blocked them.",
            "format": "int64"
          },
          "rateLimit": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "A rate limit turned them away.",
            "format": "int64"
          },
          "proofOfWork": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "The proof-of-work page, or a refusal of its token.",
            "format": "int64"
          },
          "signIn": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "An auth provider refused, redirected or failed.",
            "format": "int64"
          },
          "requestLimits": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Over a request target, header or body limit (413, 414, 431).",
            "format": "int64"
          },
          "connection": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "The connection could not serve the host: a 421, the origin lockdown or a missing client certificate.",
            "format": "int64"
          },
          "other": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "A policy with no kind of its own.",
            "format": "int64"
          },
          "unknown": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Refusals stored before the reasons were kept: counted, but of an unknown reason.",
            "format": "int64"
          },
          "noRoute": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "The host has no route for the path.",
            "format": "int64"
          },
          "unavailable": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "The 503 of no live config or no endpoint.",
            "format": "int64"
          },
          "dropped": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "The connection was dropped without an answer.",
            "format": "int64"
          },
          "unknownHost": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "No host matched the name.",
            "format": "int64"
          },
          "refused": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Everything refused: the first nine reasons.",
            "format": "int64"
          }
        },
        "additionalProperties": false,
        "description": "Requests the gate turned away, by reason. The first nine are the refusals the overview counts as refused; the last four are answers the gate gave without a policy."
      },
      "RefusalStep": {
        "required": [
          "start",
          "widthSeconds",
          "coveredSeconds",
          "refused"
        ],
        "type": "object",
        "properties": {
          "start": {
            "type": "string",
            "description": "The step's first instant.",
            "format": "date-time"
          },
          "widthSeconds": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "How long the step is.",
            "format": "int32"
          },
          "coveredSeconds": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "How many of its seconds the server observed; 0 is a time it was not running, not a step with no refusals.",
            "format": "int32"
          },
          "refused": {
            "description": "What was refused in it, by reason (counts, not rates).",
            "$ref": "#/components/schemas/RefusalReasons"
          }
        },
        "additionalProperties": false,
        "description": "One step of the refusals over time."
      },
      "ReissueRequest": {
        "type": "object",
        "properties": {
          "keySpec": {
            "enum": [
              "ecdsa-p256",
              "ecdsa-p384",
              "rsa-2048",
              "rsa-3072",
              "rsa-4096"
            ],
            "type": [
              "null",
              "string"
            ],
            "description": "The new key's kind; null keeps the current kind."
          },
          "validity": {
            "pattern": "^-?(\\d+\\.)?\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,7})?$",
            "type": [
              "null",
              "string"
            ],
            "description": "How long the new certificate is valid from now; null for the default of its source."
          }
        },
        "additionalProperties": false,
        "description": "Re-issues a self-signed or local-CA certificate under the same CertificateId and a new key: its names\nstay, and Specific bindings survive."
      },
      "RequestIdPropagation": {
        "enum": [
          "upstreamAndResponse",
          "upstreamOnly",
          "responseOnly",
          "off"
        ],
        "type": [
          "null",
          "string"
        ]
      },
      "RequestLimitsSettings": {
        "type": "object",
        "properties": {
          "maxBodySize": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "description": "Largest request body in bytes; 0 is no limit. Built-in default: 100 MiB. Over it is 413: at once when the request declares\n            its size, mid-upload when it doesn't. A route may set its own.",
            "format": "int64"
          },
          "maxHeaderCount": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "description": "Most header lines a request may carry, 8 to 128. Built-in default: 100. Host-wide: a route can't set it.",
            "format": "int32"
          },
          "maxHeaderBytes": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "description": "Most bytes the header names and values may take together, 1 KiB to 64 KiB. Built-in default: 32 KiB.\n            Host-wide: a route can't set it.",
            "format": "int32"
          },
          "maxUrlLength": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "description": "Longest request target (path and query, as sent), in bytes, 256 to 16 KiB. Built-in default: 8 KiB. Host-wide:\n            a route can't set it.",
            "format": "int32"
          }
        },
        "additionalProperties": false,
        "description": "How much of a request a host takes. Each field inherits on its own. A request over a limit is refused before the upstream\nsees it: 413 for the body, 431 for the headers, 414 for the request target. Sizes are bytes."
      },
      "RespondConfig": {
        "required": [
          "statusCode"
        ],
        "type": "object",
        "properties": {
          "statusCode": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "200-599; 204, 205 and 304 have no body.",
            "format": "int32"
          },
          "body": {
            "type": "string",
            "description": "Sent as UTF-8.",
            "default": ""
          },
          "contentType": {
            "type": "string",
            "description": "The `Content-Type` header: visible ASCII, spaces and tabs.",
            "default": "text/plain; charset=utf-8"
          }
        },
        "additionalProperties": false,
        "description": "A Respond host's fixed answer."
      },
      "RetiringLocalCaRoot": {
        "required": [
          "root",
          "leaves"
        ],
        "type": "object",
        "properties": {
          "root": {
            "description": "The old root's certificate (still downloadable).",
            "$ref": "#/components/schemas/CertificateSummary"
          },
          "leaves": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "How many certificates it signed that are still in use; the root can be retired when this is 0.",
            "format": "int32"
          }
        },
        "additionalProperties": false,
        "description": "A root a rotation replaced."
      },
      "RevisionDetail": {
        "required": [
          "revision",
          "changes",
          "document"
        ],
        "type": "object",
        "properties": {
          "revision": {
            "description": "The metadata.",
            "$ref": "#/components/schemas/RevisionSummary"
          },
          "changes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ConfigChange"
            },
            "description": "What it changed against its parent."
          },
          "document": {
            "description": "The config as of this revision, versions included.",
            "$ref": "#/components/schemas/ConfigDocument"
          }
        },
        "additionalProperties": false,
        "description": "A revision with what it changed and its whole document."
      },
      "RevisionDiff": {
        "required": [
          "from",
          "to",
          "changes"
        ],
        "type": "object",
        "properties": {
          "from": {
            "description": "The older side.",
            "$ref": "#/components/schemas/ConfigRevisionId"
          },
          "to": {
            "description": "The newer side.",
            "$ref": "#/components/schemas/ConfigRevisionId"
          },
          "changes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ConfigChange"
            },
            "description": "The structural diff, as a dry run reports it."
          }
        },
        "additionalProperties": false,
        "description": "What changed from one revision to another."
      },
      "RevisionRef": {
        "required": [
          "id",
          "seq"
        ],
        "type": "object",
        "properties": {
          "id": {
            "description": "The revision.",
            "$ref": "#/components/schemas/ConfigRevisionId"
          },
          "seq": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "1, 2, 3, ... in commit order; shown as \"#42\".",
            "format": "int64"
          }
        },
        "additionalProperties": false,
        "description": "A committed revision, by ID and display number."
      },
      "RevisionSummary": {
        "required": [
          "id",
          "seq",
          "actor",
          "channel",
          "createdAt",
          "documentSha256"
        ],
        "type": "object",
        "properties": {
          "id": {
            "description": "The revision.",
            "$ref": "#/components/schemas/ConfigRevisionId"
          },
          "seq": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "1, 2, 3, ... in commit order; shown as \"#42\".",
            "format": "int64"
          },
          "actor": {
            "description": "Who made the change.",
            "$ref": "#/components/schemas/ActorRef"
          },
          "channel": {
            "description": "Where it came in.",
            "$ref": "#/components/schemas/Channel"
          },
          "createdAt": {
            "type": "string",
            "description": "Commit time (UTC).",
            "format": "date-time"
          },
          "documentSha256": {
            "type": "string",
            "description": "SHA-256 of the canonical document, lowercase hex."
          },
          "parentId": {
            "examples": [
              "rev_0A1b2C3d4E5f6G7h8I9j0K"
            ],
            "pattern": "^rev_[0-9A-Za-z]{22}$",
            "type": [
              "null",
              "string"
            ],
            "description": "The revision it was made on top of; null for the first."
          },
          "message": {
            "type": [
              "null",
              "string"
            ],
            "description": "The optional note."
          }
        },
        "additionalProperties": false,
        "description": "A committed revision's metadata."
      },
      "RobotsMode": {
        "enum": [
          "passthrough",
          "disallowAll",
          "blockAiCrawlers",
          "custom"
        ],
        "type": [
          "null",
          "string"
        ]
      },
      "RobotsSettings": {
        "type": "object",
        "properties": {
          "mode": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Passthrough, a preset, or the operator's text. Built-in default: passthrough.",
                "$ref": "#/components/schemas/RobotsMode"
              }
            ]
          },
          "customText": {
            "type": [
              "null",
              "string"
            ],
            "description": "The text RobotsMode.Custom serves. Set with that mode at its layer, or inherited from one that set it;\nany other mode ignores it. Line endings are normalised when the proxy compiles it, not in the stored document."
          }
        },
        "additionalProperties": false,
        "description": "What the host answers at `/robots.txt`. There is no route-level override: a host has one file, and every route\nshares it. Each field inherits on its own, so a layer can change the mode and keep the text below it."
      },
      "RotateDnsCredentialBody": {
        "required": [
          "credential"
        ],
        "type": "object",
        "properties": {
          "credential": {
            "type": "string",
            "description": "The new API token."
          }
        },
        "additionalProperties": false,
        "description": "Replaces a DNS provider's credential, keeping the provider (and everything that uses it)."
      },
      "RouteConfig": {
        "required": [
          "id",
          "upstreamId"
        ],
        "type": "object",
        "properties": {
          "id": {
            "description": "Identity; unique across all hosts.",
            "$ref": "#/components/schemas/RouteId"
          },
          "upstreamId": {
            "description": "The upstream to proxy to.",
            "$ref": "#/components/schemas/UpstreamId"
          },
          "order": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "A specificity tiebreaker: lower runs first between routes of equal match kind and path length;\n            equal orders keep their listed order.",
            "format": "int32",
            "default": 0
          },
          "match": {
            "enum": [
              "prefix",
              "exact"
            ],
            "type": "string",
            "description": "How Path matches.",
            "default": "prefix"
          },
          "path": {
            "type": "string",
            "description": "Starts with `/`; decoded and canonical (no `\\`, `//`, `;`, dot segments,\n            percent-encoding, query or fragment). Matching ignores case, but the path keeps the case it was written in; two\n            routes of a host with the same match kind can't have paths that differ only in case.",
            "default": "/"
          },
          "rewrite": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "How the path the upstream receives differs from the client's; null forwards it as received.",
                "$ref": "#/components/schemas/PathRewriteConfig"
              }
            ]
          },
          "settings": {
            "$ref": "#/components/schemas/RouteSettings"
          }
        },
        "additionalProperties": false,
        "description": "One route of a proxy host."
      },
      "RouteId": {
        "examples": [
          "rte_0A1b2C3d4E5f6G7h8I9j0K"
        ],
        "pattern": "^rte_[0-9A-Za-z]{22}$",
        "type": "string",
        "description": "A route of a proxy host."
      },
      "RouteSettings": {
        "type": "object",
        "properties": {
          "proofOfWork": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "The route-level subset of ProofOfWorkSettings: only bool? ProofOfWorkSettings.Enabled is\naccepted here. It overrides the host's resolved switch either way, so one path can be exempt or protected.",
                "$ref": "#/components/schemas/ProofOfWorkSettings"
              }
            ]
          },
          "rateLimit": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "The route-level subset of RateLimitSettings: only bool? RateLimitSettings.Enabled and\nRateLimitPolicyId? RateLimitSettings.PolicyId are accepted here, field by field over the host's resolved values. So a\nroute can inherit its host's policy, name another one, or turn the limit off for its own paths.",
                "$ref": "#/components/schemas/RateLimitSettings"
              }
            ]
          },
          "accessList": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Field by field over the host's resolved list: a route inherits the host's, names another, or turns the list\n            off for its own paths.",
                "$ref": "#/components/schemas/AccessListSettings"
              }
            ]
          },
          "requestLimits": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "The route-level subset of RequestLimitsSettings: only long? RequestLimitsSettings.MaxBodySize is\naccepted here, over the host's resolved value, so an upload route can take more (or less) than the rest of the host.\nThe header and URL limits are judged before a route is known, so they belong to the host.",
                "$ref": "#/components/schemas/RequestLimitsSettings"
              }
            ]
          },
          "ruleSet": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Field by field over the host's resolved set: a route inherits the host's, names another, or turns the rules off\n            for its own paths.",
                "$ref": "#/components/schemas/RuleSetSettings"
              }
            ]
          },
          "authProvider": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Field by field over the host's resolved provider: a route inherits the host's, names another, or turns authentication off\n            for its own paths (the route that serves the provider's sign-in pages has to).",
                "$ref": "#/components/schemas/AuthProviderSettings"
              }
            ]
          },
          "headers": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Rule by rule over the host's resolved rules: a route's rule for a header name replaces the host's for it.",
                "$ref": "#/components/schemas/HeadersSettings"
              }
            ]
          }
        },
        "additionalProperties": false,
        "description": "The route layer: the route-level subset of HostSettings, on top of the host's resolved\n            settings. Null fields inherit from the host."
      },
      "RuleAction": {
        "enum": [
          "deny",
          "drop",
          "allow"
        ],
        "type": "string",
        "description": "What a rule set does with a request its rule matches."
      },
      "RuleConfig": {
        "required": [
          "action"
        ],
        "type": "object",
        "properties": {
          "action": {
            "description": "What happens to a request the rule matches.",
            "$ref": "#/components/schemas/RuleAction"
          },
          "match": {
            "$ref": "#/components/schemas/RuleMatchConfig"
          }
        },
        "additionalProperties": false,
        "description": "One rule: every condition in RuleMatchConfig RuleConfig.Match must hold for it to apply."
      },
      "RuleMatchConfig": {
        "type": "object",
        "properties": {
          "path": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "The path pattern.",
                "$ref": "#/components/schemas/RulePathConfig"
              }
            ]
          },
          "methods": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "type": "string"
            },
            "description": "HTTP methods, upper case (canonical order: sorted, no duplicates); null = any."
          },
          "userAgent": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "The user-agent condition.",
                "$ref": "#/components/schemas/RuleUserAgentConfig"
              }
            ]
          }
        },
        "additionalProperties": false,
        "description": "The conditions of a rule; one that is left out isn't tested."
      },
      "RulePathConfig": {
        "required": [
          "kind",
          "pattern"
        ],
        "type": "object",
        "properties": {
          "kind": {
            "description": "Exact, prefix, glob or regex.",
            "$ref": "#/components/schemas/RulePathKind"
          },
          "pattern": {
            "type": "string",
            "description": "Exact, prefix and glob start with `/` and are written decoded, in the canonical form a route's\n            path takes. A regex is matched without backtracking against the canonical path, ignoring case."
          }
        },
        "additionalProperties": false,
        "description": "A path condition: how to read Pattern and the pattern itself."
      },
      "RulePathKind": {
        "enum": [
          "exact",
          "prefix",
          "glob",
          "regex"
        ],
        "type": "string",
        "description": "How a rule's path condition reads its pattern. All four compare the canonical matching path ignoring ASCII case."
      },
      "RuleSetConfig": {
        "required": [
          "id",
          "name"
        ],
        "type": "object",
        "properties": {
          "id": {
            "description": "Identity. The built-in presets (RuleSetPresets) have reserved IDs and are never part of a document.",
            "$ref": "#/components/schemas/RuleSetId"
          },
          "name": {
            "type": "string",
            "description": "Human label, unique ignoring case."
          },
          "version": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Concurrency version.",
            "format": "int64",
            "default": 0
          },
          "notes": {
            "type": [
              "null",
              "string"
            ],
            "description": "Free text."
          },
          "rules": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/RuleConfig"
            }
          }
        },
        "additionalProperties": false,
        "description": "A named, ordered list of request rules that hosts and routes apply: each rule names a path, methods and a user agent a\nrequest must all match, and what to do with one that does. The first rule that matches decides; a request no rule matches\ngoes on through the rest of the chain. It is a top-level object, so one set can guard several hosts and routes."
      },
      "RuleSetId": {
        "examples": [
          "rs_0A1b2C3d4E5f6G7h8I9j0K"
        ],
        "pattern": "^rs_[0-9A-Za-z]{22}$",
        "type": "string",
        "description": "A rule set: an ordered list of path, method and user-agent rules, which hosts and routes name. The built-in presets\n            have reserved IDs."
      },
      "RuleSetOverride": {
        "required": [
          "routes"
        ],
        "type": "object",
        "properties": {
          "routes": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "How many routes.",
            "format": "int32"
          },
          "set": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "The set those routes apply; absent when they apply none.",
                "$ref": "#/components/schemas/RuleSetRef"
              }
            ]
          }
        },
        "additionalProperties": false,
        "description": "Routes that apply another rule set, or none."
      },
      "RuleSetProtection": {
        "required": [
          "overrides"
        ],
        "type": "object",
        "properties": {
          "overrides": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RuleSetOverride"
            },
            "description": "The routes that differ from the host's set, grouped by what applies to them instead."
          },
          "set": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "The set the host names; absent when it names none.",
                "$ref": "#/components/schemas/RuleSetRef"
              }
            ]
          }
        },
        "additionalProperties": false,
        "description": "A host's rule-set setting."
      },
      "RuleSetRef": {
        "required": [
          "id",
          "name",
          "rules",
          "builtIn",
          "denied",
          "dropped",
          "allowed"
        ],
        "type": "object",
        "properties": {
          "id": {
            "description": "The set.",
            "$ref": "#/components/schemas/RuleSetId"
          },
          "name": {
            "type": "string",
            "description": "Its name."
          },
          "rules": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "How many rules it has.",
            "format": "int32"
          },
          "builtIn": {
            "type": "boolean",
            "description": "It is one of the built-in presets, which have no page of their own to open."
          },
          "denied": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Requests a rule refused with 403 over the range, across every host that applies the set.",
            "format": "int64"
          },
          "dropped": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Requests a rule dropped without an answer.",
            "format": "int64"
          },
          "allowed": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Requests an allow rule took out of the set's later rules.",
            "format": "int64"
          },
          "timeouts": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Regex matches that ran past their timeout and were decided by the rule's action (a deny or drop counts as a match, an allow as none); 0 in a healthy proxy.",
            "format": "int64",
            "default": 0
          }
        },
        "additionalProperties": false,
        "description": "A named rule set with what it has done since the process started, across every host and route that applies it."
      },
      "RuleSetSettings": {
        "type": "object",
        "properties": {
          "enabled": {
            "type": [
              "null",
              "boolean"
            ],
            "description": "Apply the resolved set. Built-in default: on. Off lifts an inherited set."
          },
          "ruleSetId": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "The set to apply; null = inherit. With bool? RuleSetSettings.Enabled off the reference is kept but inert.",
                "$ref": "#/components/schemas/RuleSetId"
              }
            ]
          }
        },
        "additionalProperties": false,
        "description": "Which rule set a layer applies. RuleSetId? RuleSetSettings.RuleSetId names a set the document declares or a built-in preset;\n            both fields inherit on their own, so a host can turn an inherited set off without forgetting which one it was, and a route\n            can inherit its host's, name another, or turn it off. A layer that names a set without stating bool? RuleSetSettings.Enabled\n            leaves the switch to the built-in default, which is on. One set per layer: several concerns are several rules in one set."
      },
      "RuleUserAgentConfig": {
        "type": "object",
        "properties": {
          "empty": {
            "type": "boolean",
            "description": "Also match a missing or empty user agent.",
            "default": false
          },
          "contains": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "type": "string"
            }
          }
        },
        "additionalProperties": false,
        "description": "A user-agent condition: it holds when the header contains any of Contains, or when\n            Empty is set and the request has no user agent or an empty one."
      },
      "SecondFactorRequest": {
        "type": "object",
        "properties": {
          "code": {
            "type": [
              "null",
              "string"
            ],
            "description": "The six digits from the authenticator."
          },
          "recoveryCode": {
            "type": [
              "null",
              "string"
            ],
            "description": "A single-use recovery code."
          }
        },
        "additionalProperties": false,
        "description": "The second step: exactly one of a TOTP code or a recovery code."
      },
      "SecurityOverview": {
        "required": [
          "range",
          "sampledAt",
          "proofOfWork",
          "rateLimit",
          "accessLists",
          "hosts",
          "forwardAuth",
          "connections",
          "refusals",
          "series",
          "step"
        ],
        "type": "object",
        "properties": {
          "range": {
            "description": "How far back the counters reach: the hour is the sampler's memory (and the hour before a restart), the rest the one-minute rollups.",
            "$ref": "#/components/schemas/MetricsRange"
          },
          "sampledAt": {
            "type": "string",
            "description": "When it was read.",
            "format": "date-time"
          },
          "proofOfWork": {
            "description": "The proof-of-work gate's counters over the range.",
            "$ref": "#/components/schemas/ProofOfWorkCounters"
          },
          "rateLimit": {
            "description": "The rate limiter's counters over the range.",
            "$ref": "#/components/schemas/RateLimitCounters"
          },
          "accessLists": {
            "description": "The access lists' counters over the range.",
            "$ref": "#/components/schemas/AccessListCounters"
          },
          "hosts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/HostProtections"
            },
            "description": "The enabled hosts, by name, with what each refused over the range."
          },
          "forwardAuth": {
            "description": "Every auth provider's counters over the range, summed, a deleted provider's included.",
            "$ref": "#/components/schemas/SignInCounters"
          },
          "connections": {
            "description": "The connection stage: its limit and deny list as configured now, what it refused over the range, and what it counts now.",
            "$ref": "#/components/schemas/ConnectionCounters"
          },
          "refusals": {
            "description": "What the gate refused over the range, by reason.",
            "$ref": "#/components/schemas/RefusalReasons"
          },
          "series": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RefusalStep"
            },
            "description": "The refusals by reason over time, one step per Step (the hour mixes minutes from before a restart with ten-second steps): every step from the first the server observed to the last, a step with `coveredSeconds` 0 being a time it was not running."
          },
          "step": {
            "pattern": "^-?(\\d+\\.)?\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,7})?$",
            "type": "string",
            "description": "The width of a step of Series."
          },
          "from": {
            "type": [
              "null",
              "string"
            ],
            "description": "The first moment inside the range the server observed: the counters cover from here to SampledAt, less any time it was down. Absent when it observed none of the range.",
            "format": "date-time"
          }
        },
        "additionalProperties": false,
        "description": "What protects the hosts and how much work it has done: the gate's counters over a range, kept in the metrics' rollups so a restart\ndoesn't reset them, the refusals by reason and over time, and each enabled host's protections as the live snapshot compiled them."
      },
      "SelfSignedCertificateRequest": {
        "required": [
          "names"
        ],
        "type": "object",
        "properties": {
          "names": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "DNS names, `*.` wildcards and IP addresses; at least one. The first is the common name when it is a DNS name of at most 64 characters."
          },
          "keySpec": {
            "enum": [
              "ecdsa-p256",
              "ecdsa-p384",
              "rsa-2048",
              "rsa-3072",
              "rsa-4096"
            ],
            "type": "string",
            "description": "The kind of key the vault generates, on the wire its stable name (`ecdsa-p256`, `ecdsa-p384`, `rsa-2048`, `rsa-3072`, `rsa-4096`).",
            "default": "ecdsa-p256"
          },
          "validity": {
            "pattern": "^-?(\\d+\\.)?\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,7})?$",
            "type": [
              "null",
              "string"
            ],
            "description": "How long it is valid from now (at most 825 days); null for the default (one year)."
          },
          "label": {
            "type": [
              "null",
              "string"
            ],
            "description": "A display name."
          }
        },
        "additionalProperties": false,
        "description": "A new self-signed certificate. Public data only: the key is made in the vault and never appears here."
      },
      "ServerHeaderMode": {
        "enum": [
          "paduraksa",
          "custom",
          "upstream",
          "remove"
        ],
        "type": [
          "null",
          "string"
        ]
      },
      "ServerHeaderSettings": {
        "type": "object",
        "properties": {
          "mode": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Which header wins. Built-in default: ServerHeaderMode.Paduraksa, the name without a\n            version.",
                "$ref": "#/components/schemas/ServerHeaderMode"
              }
            ]
          },
          "value": {
            "type": [
              "null",
              "string"
            ],
            "description": "The header ServerHeaderMode.Custom sends: 1-128 visible ASCII characters or spaces, with no\n            leading or trailing space. Required once the resolved mode is ServerHeaderMode.Custom; ignored by\n            every other mode."
          }
        },
        "additionalProperties": false,
        "description": "What the host's responses say in `Server`. ServerHeaderMode? ServerHeaderSettings.Mode and string? ServerHeaderSettings.Value inherit on\ntheir own, like every other nested setting."
      },
      "ServerSettingApply": {
        "enum": [
          "live",
          "restart"
        ],
        "type": "string",
        "description": "When a changed value takes effect."
      },
      "ServerSettingGroup": {
        "enum": [
          "retention",
          "signIn",
          "notifications",
          "integrations",
          "connections"
        ],
        "type": "string",
        "description": "Where a setting sits on the server settings page."
      },
      "ServerSettingKind": {
        "enum": [
          "duration",
          "number",
          "size",
          "boolean",
          "text"
        ],
        "type": "string",
        "description": "What kind of value a server setting holds, which says how it is written as text and edited."
      },
      "ServerSettingSource": {
        "enum": [
          "default",
          "config",
          "environment"
        ],
        "type": "string",
        "description": "Where the value in force comes from."
      },
      "ServerSettingView": {
        "required": [
          "key",
          "group",
          "section",
          "kind",
          "apply",
          "title",
          "description",
          "configurationKey",
          "environmentVariable",
          "default",
          "value",
          "source",
          "restartPending"
        ],
        "type": "object",
        "properties": {
          "key": {
            "type": "string",
            "description": "Its member in ServerSettings."
          },
          "group": {
            "description": "Where the page shows it.",
            "$ref": "#/components/schemas/ServerSettingGroup"
          },
          "section": {
            "type": "string",
            "description": "Its heading within the group."
          },
          "kind": {
            "description": "The kind of value.",
            "$ref": "#/components/schemas/ServerSettingKind"
          },
          "apply": {
            "description": "Whether a change applies live or after a restart.",
            "$ref": "#/components/schemas/ServerSettingApply"
          },
          "title": {
            "type": "string",
            "description": "Its name."
          },
          "description": {
            "type": "string",
            "description": "What it does."
          },
          "configurationKey": {
            "type": "string",
            "description": "The appsettings key (`Paduraksa:Metrics:Retention`)."
          },
          "environmentVariable": {
            "type": "string",
            "description": "The environment variable that sets it, and then wins over the config."
          },
          "default": {
            "type": "string",
            "description": "The built-in default."
          },
          "value": {
            "type": "string",
            "description": "The value in force."
          },
          "source": {
            "description": "Where Value comes from.",
            "$ref": "#/components/schemas/ServerSettingSource"
          },
          "restartPending": {
            "type": "boolean",
            "description": "The value in force differs from Running: a restart applies it."
          },
          "min": {
            "type": [
              "null",
              "string"
            ],
            "description": "The least value accepted, for an ordered kind."
          },
          "max": {
            "type": [
              "null",
              "string"
            ],
            "description": "The largest value accepted, for an ordered kind."
          },
          "stored": {
            "type": [
              "null",
              "string"
            ],
            "description": "What the config stores; null when it leaves the setting to the default."
          },
          "running": {
            "type": [
              "null",
              "string"
            ],
            "description": "For a setting that applies after a restart: the value the process runs with."
          }
        },
        "additionalProperties": false,
        "description": "One server setting. Values are text in the form the config writes them (`90.00:00:00`, `1073741824`, `true`)."
      },
      "ServerSettings": {
        "type": "object",
        "properties": {
          "metricsRetention": {
            "pattern": "^-?(\\d+\\.)?\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,7})?$",
            "type": [
              "null",
              "string"
            ]
          },
          "analyticsRetention": {
            "pattern": "^-?(\\d+\\.)?\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,7})?$",
            "type": [
              "null",
              "string"
            ]
          },
          "notificationHistoryRetention": {
            "pattern": "^-?(\\d+\\.)?\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,7})?$",
            "type": [
              "null",
              "string"
            ]
          },
          "logRetainedFiles": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "format": "int64"
          },
          "logMaxAge": {
            "pattern": "^-?(\\d+\\.)?\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,7})?$",
            "type": [
              "null",
              "string"
            ]
          },
          "logMaxTotalSize": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "format": "int64"
          },
          "logMaxFileSize": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "format": "int64"
          },
          "adminMaxFailedAttempts": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "format": "int64"
          },
          "adminLockoutDuration": {
            "pattern": "^-?(\\d+\\.)?\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,7})?$",
            "type": [
              "null",
              "string"
            ]
          },
          "adminSessionLifetime": {
            "pattern": "^-?(\\d+\\.)?\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,7})?$",
            "type": [
              "null",
              "string"
            ]
          },
          "adminSessionIdleTimeout": {
            "pattern": "^-?(\\d+\\.)?\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,7})?$",
            "type": [
              "null",
              "string"
            ]
          },
          "adminPartialSessionLifetime": {
            "pattern": "^-?(\\d+\\.)?\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,7})?$",
            "type": [
              "null",
              "string"
            ]
          },
          "adminMinPasswordLength": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "format": "int64"
          },
          "adminTotpIssuer": {
            "type": [
              "null",
              "string"
            ]
          },
          "adminRateLimitPermitsPerIp": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "format": "int64"
          },
          "adminRateLimitPermitsPerUser": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "format": "int64"
          },
          "adminRateLimitWindow": {
            "pattern": "^-?(\\d+\\.)?\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,7})?$",
            "type": [
              "null",
              "string"
            ]
          },
          "notificationAdminUrl": {
            "type": [
              "null",
              "string"
            ]
          },
          "notificationHealthHold": {
            "pattern": "^-?(\\d+\\.)?\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,7})?$",
            "type": [
              "null",
              "string"
            ]
          },
          "notificationCoalesceWindow": {
            "pattern": "^-?(\\d+\\.)?\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,7})?$",
            "type": [
              "null",
              "string"
            ]
          },
          "notificationMaxDeliveryAge": {
            "pattern": "^-?(\\d+\\.)?\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,7})?$",
            "type": [
              "null",
              "string"
            ]
          },
          "accessLogQueryString": {
            "type": [
              "null",
              "boolean"
            ]
          },
          "cloudflareRangeRefresh": {
            "type": [
              "null",
              "boolean"
            ]
          },
          "cloudflareRangeInterval": {
            "pattern": "^-?(\\d+\\.)?\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,7})?$",
            "type": [
              "null",
              "string"
            ]
          },
          "acmeDnsPropagationDelay": {
            "pattern": "^-?(\\d+\\.)?\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,7})?$",
            "type": [
              "null",
              "string"
            ]
          },
          "acmeDnsPropagationCheck": {
            "type": [
              "null",
              "boolean"
            ]
          },
          "mcpEnabled": {
            "type": [
              "null",
              "boolean"
            ]
          },
          "mcpIdleTimeout": {
            "pattern": "^-?(\\d+\\.)?\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,7})?$",
            "type": [
              "null",
              "string"
            ]
          },
          "limitsRequestHeadersTimeout": {
            "pattern": "^-?(\\d+\\.)?\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,7})?$",
            "type": [
              "null",
              "string"
            ]
          },
          "limitsKeepAliveTimeout": {
            "pattern": "^-?(\\d+\\.)?\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,7})?$",
            "type": [
              "null",
              "string"
            ]
          },
          "limitsTlsHandshakeTimeout": {
            "pattern": "^-?(\\d+\\.)?\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,7})?$",
            "type": [
              "null",
              "string"
            ]
          },
          "limitsMinRequestBodyRate": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "format": "int64"
          },
          "limitsMinRequestBodyGrace": {
            "pattern": "^-?(\\d+\\.)?\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,7})?$",
            "type": [
              "null",
              "string"
            ]
          },
          "limitsMinResponseRate": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "format": "int64"
          },
          "limitsMinResponseGrace": {
            "pattern": "^-?(\\d+\\.)?\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,7})?$",
            "type": [
              "null",
              "string"
            ]
          },
          "limitsMaxConnections": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "format": "int64"
          },
          "limitsMaxUpgradedConnections": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "format": "int64"
          }
        },
        "additionalProperties": false,
        "description": "The server's own settings that the dashboard may change (docs/design/config.md \"Server settings\"): how long data is kept, the sign-in\npolicy and the like, as opposed to how requests are routed. Part of GlobalSettings, so a change is a revision with its audit\nevent, its diff and its undo. Every field is nullable: null is the built-in default. A value the server's environment sets\nexplicitly (`Paduraksa__Metrics__Retention`) wins over the stored one."
      },
      "ServerSettingsView": {
        "required": [
          "version",
          "stored",
          "settings"
        ],
        "type": "object",
        "properties": {
          "version": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "The global settings' version: the `If-Match` a change needs.",
            "format": "int64"
          },
          "stored": {
            "description": "What the config stores (null fields are left to the default or the environment): the body a change replaces.",
            "$ref": "#/components/schemas/ServerSettings"
          },
          "settings": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ServerSettingView"
            },
            "description": "Every setting, in the page's order."
          }
        },
        "additionalProperties": false,
        "description": "The server settings as the API and the dashboard show them: every declared setting with its value in force and where it comes from."
      },
      "SessionId": {
        "examples": [
          "ses_0A1b2C3d4E5f6G7h8I9j0K"
        ],
        "pattern": "^ses_[0-9A-Za-z]{22}$",
        "type": "string",
        "description": "An admin session. UuidV4: a session's ID is shown in the session list and handed to the dashboard, so it\nshould not tell when the session began. It is a handle, not a credential: the cookie value is the credential, a\nseparate 256-bit random value of which only the hash is stored."
      },
      "SessionStarted": {
        "required": [
          "sessionId",
          "secondFactorRequired",
          "expiresAt"
        ],
        "type": "object",
        "properties": {
          "sessionId": {
            "description": "The session (a handle, not the cookie).",
            "$ref": "#/components/schemas/SessionId"
          },
          "secondFactorRequired": {
            "type": "boolean",
            "description": "The password was right but a second factor is owed: only `auth/second-factor` accepts this session until it is given."
          },
          "expiresAt": {
            "type": "string",
            "description": "When the session ends (when the partial session ends, if a second factor is owed).",
            "format": "date-time"
          }
        },
        "additionalProperties": false,
        "description": "A session that was just started, by signing in or by setup. Its cookie travels in `Set-Cookie` only, never in a body."
      },
      "SessionSummary": {
        "required": [
          "id",
          "clientIp",
          "userAgent",
          "createdAt",
          "lastSeenAt",
          "expiresAt",
          "secondFactorCompleted",
          "current"
        ],
        "type": "object",
        "properties": {
          "id": {
            "description": "The session (a handle, not the cookie).",
            "$ref": "#/components/schemas/SessionId"
          },
          "clientIp": {
            "type": [
              "null",
              "string"
            ],
            "description": "Where it was created from."
          },
          "userAgent": {
            "type": [
              "null",
              "string"
            ],
            "description": "The browser that created it."
          },
          "createdAt": {
            "type": "string",
            "description": "When it began.",
            "format": "date-time"
          },
          "lastSeenAt": {
            "type": "string",
            "description": "When it was last used (to within the touch interval).",
            "format": "date-time"
          },
          "expiresAt": {
            "type": "string",
            "description": "Its absolute end; it ends earlier if idle too long.",
            "format": "date-time"
          },
          "secondFactorCompleted": {
            "type": "boolean",
            "description": "False for a session still waiting for the second factor."
          },
          "current": {
            "type": "boolean",
            "description": "The session that asked for the list."
          }
        },
        "additionalProperties": false,
        "description": "An active session, for the session list."
      },
      "SetupCompleted": {
        "required": [
          "owner",
          "session"
        ],
        "type": "object",
        "properties": {
          "owner": {
            "description": "The new Owner.",
            "$ref": "#/components/schemas/UserSummary"
          },
          "session": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "The session the response sets a cookie for; null only if signing the new Owner in failed, in which case they sign in as usual.",
                "$ref": "#/components/schemas/SessionStarted"
              }
            ]
          }
        },
        "additionalProperties": false,
        "description": "The result of first-run setup: the new Owner, signed in."
      },
      "SetupRequest": {
        "required": [
          "setupToken",
          "username",
          "password"
        ],
        "type": "object",
        "properties": {
          "setupToken": {
            "type": "string",
            "description": "The one-time token from the server log or `paduraksa setup-token`."
          },
          "username": {
            "type": "string",
            "description": "The Owner's sign-in name."
          },
          "password": {
            "type": "string",
            "description": "The Owner's password."
          },
          "displayName": {
            "type": [
              "null",
              "string"
            ],
            "description": "Shown in the UI; the username when null or empty."
          }
        },
        "additionalProperties": false,
        "description": "Creates the first Owner."
      },
      "SetupStatus": {
        "required": [
          "complete"
        ],
        "type": "object",
        "properties": {
          "complete": {
            "type": "boolean",
            "description": "An Owner exists."
          }
        },
        "additionalProperties": false,
        "description": "Whether the first Owner exists. Until it does, the admin app serves only the setup flow."
      },
      "SignInCounters": {
        "required": [
          "allowed",
          "denied",
          "redirected",
          "cacheHits",
          "calls",
          "errors",
          "timeouts",
          "busy"
        ],
        "type": "object",
        "properties": {
          "allowed": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Requests a provider let through, from its answer or from its decision cache.",
            "format": "int64"
          },
          "denied": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Requests a provider refused (a relayed 401, 403 or 407).",
            "format": "int64"
          },
          "redirected": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Requests sent to a sign-in page.",
            "format": "int64"
          },
          "cacheHits": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Allows answered from the decision cache, with no call.",
            "format": "int64"
          },
          "calls": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Calls made to the providers' endpoints.",
            "format": "int64"
          },
          "errors": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Calls that failed or got an answer the proxy refuses to act on (502).",
            "format": "int64"
          },
          "timeouts": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Calls that ran out of time (504).",
            "format": "int64"
          },
          "busy": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Requests refused with 503 because too many calls were already running.",
            "format": "int64"
          }
        },
        "additionalProperties": false,
        "description": "The auth providers' counters, summed over every provider the live config names."
      },
      "SimulateBody": {
        "required": [
          "request"
        ],
        "type": "object",
        "properties": {
          "request": {
            "description": "The request to ask \"what would this get, and why\" about.",
            "$ref": "#/components/schemas/SimulationRequest"
          },
          "target": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "The config to simulate against; null for the live one.",
                "$ref": "#/components/schemas/SimulationTarget"
              }
            ]
          }
        },
        "additionalProperties": false,
        "description": "The body of `POST /api/v1/simulate`: which config to run the request against, and the request."
      },
      "SimulationDestination": {
        "required": [
          "id",
          "address",
          "health"
        ],
        "type": "object",
        "properties": {
          "id": {
            "description": "The destination.",
            "$ref": "#/components/schemas/DestinationId"
          },
          "address": {
            "type": "string",
            "description": "Its address."
          },
          "health": {
            "description": "Its health in the <b>live</b> proxy: health is live state, so for a draft or revision it is the live\n            proxy's answer for the destination with the same ID.",
            "$ref": "#/components/schemas/SimulationHealth"
          }
        },
        "additionalProperties": false,
        "description": "A destination of the route's upstream, as the target config lists it."
      },
      "SimulationErrorPage": {
        "required": [
          "custom",
          "source",
          "mode"
        ],
        "type": "object",
        "properties": {
          "custom": {
            "type": "boolean",
            "description": "An operator's page. False: the page built into Paduraksa."
          },
          "source": {
            "description": "Which layer the operator's page came from (the global defaults, the profile, the host); built-in when it isn't one.",
            "$ref": "#/components/schemas/ErrorPageSource"
          },
          "mode": {
            "description": "A message in the built-in layout, or the operator's own document.",
            "$ref": "#/components/schemas/ErrorPageMode"
          }
        },
        "additionalProperties": false,
        "description": "Which page an answer is served as, to a client that accepts HTML (anything else gets plain text)."
      },
      "SimulationFact": {
        "required": [
          "name",
          "value"
        ],
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "What it is (`peer`, `header`, `match`, ...)."
          },
          "value": {
            "type": "string",
            "description": "Its value."
          }
        },
        "additionalProperties": false,
        "description": "A detail of a step."
      },
      "SimulationHeader": {
        "required": [
          "name",
          "value"
        ],
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "The header name."
          },
          "value": {
            "type": "string",
            "description": "The value."
          }
        },
        "additionalProperties": false,
        "description": "A request header, as sent."
      },
      "SimulationHealth": {
        "enum": [
          "healthy",
          "unhealthy",
          "unknown"
        ],
        "type": "string",
        "description": "What is known about a destination's health."
      },
      "SimulationOutcome": {
        "required": [
          "kind",
          "summary"
        ],
        "type": "object",
        "properties": {
          "kind": {
            "description": "What happens.",
            "$ref": "#/components/schemas/SimulationOutcomeKind"
          },
          "summary": {
            "type": "string",
            "description": "Why, in one line: the step that decided."
          },
          "statusCode": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "description": "The status, when the gate answers.",
            "format": "int32"
          },
          "location": {
            "type": [
              "null",
              "string"
            ],
            "description": "The `Location` of a redirect, built as the real response builds it."
          },
          "body": {
            "type": [
              "null",
              "string"
            ],
            "description": "The body of a short text answer."
          },
          "errorPage": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "The page a browser is served when the answer is one of the proxy's own error pages; absent for a redirect, a\n            forward, a drop and an answer that is only text (a Respond host's).",
                "$ref": "#/components/schemas/SimulationErrorPage"
              }
            ]
          }
        },
        "additionalProperties": false,
        "description": "The gate's verdict."
      },
      "SimulationOutcomeKind": {
        "enum": [
          "forwardToUpstream",
          "fixedAnswer",
          "redirect",
          "misdirectedRequest",
          "drop",
          "acmeChallengeAnswer",
          "noRoute",
          "defaultHost",
          "handshakeRejected",
          "connectionRejected",
          "rejectedByServer"
        ],
        "type": "string",
        "description": "What the gate does with the request."
      },
      "SimulationRequest": {
        "required": [
          "url",
          "clientIp"
        ],
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "description": "The absolute `http` or `https` URL: scheme, host, port, path and query. The `Host`\n            header is its authority, unless Headers sets one."
          },
          "clientIp": {
            "type": "string",
            "description": "The client's address (IPv4 or IPv6). Behind a proxy (Arrival), the address the\n            proxy reports."
          },
          "method": {
            "type": [
              "null",
              "string"
            ],
            "description": "Default `GET`. Case matters, as on the wire: `get` is not `GET`."
          },
          "arrival": {
            "enum": [
              "direct",
              "trustedProxy",
              "cloudflareProxy",
              "cloudflareTunnel"
            ],
            "type": "string",
            "description": "How the request reaches Paduraksa. Default: directly.",
            "default": "direct"
          },
          "peerIp": {
            "type": [
              "null",
              "string"
            ],
            "description": "The connection-level peer, for a request that comes through a proxy (Arrival other\n            than direct) or with ProxyProtocol. Default: an address the config trusts as that kind of proxy\n            (loopback when it trusts none). A direct request's peer is the client, so setting it there is refused."
          },
          "proxyProtocol": {
            "type": "boolean",
            "description": "A PROXY protocol header arrived first: PeerIp is the address it carried.\n            The listener must expect one: otherwise the connection is rejected, as the real one would be.",
            "default": false
          },
          "transportIp": {
            "type": [
              "null",
              "string"
            ],
            "description": "With ProxyProtocol only: the socket's remote address (the load balancer; it must\n            be one of the listener's trusted sources). Default: loopback."
          },
          "tls": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "The TLS context; `https` URLs only (a plain-HTTP listener speaks HTTP/1.1 and has no handshake).\n            Default: SNI from the URL, HTTP/2, TLS 1.3.",
                "$ref": "#/components/schemas/SimulationTls"
              }
            ]
          },
          "country": {
            "type": [
              "null",
              "string"
            ],
            "description": "The country Cloudflare would report for the client (`CF-IPCountry`): a two-letter ISO 3166-1 code, `T1`\n            for Tor or `XX` for unknown. Only a request that arrives through Cloudflare (Arrival) carries it; to see\n            the gate ignore one a client forged, send a `CF-IPCountry` header on another arrival. Default: no header, so the country is unknown."
          },
          "headers": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/SimulationHeader"
            }
          }
        },
        "additionalProperties": false,
        "description": "A request to ask \"what would this get, and why\" about. A URL and a client IP are enough; everything else\ndefaults to a plain GET from a client that connects directly."
      },
      "SimulationResult": {
        "required": [
          "status",
          "source"
        ],
        "type": "object",
        "properties": {
          "status": {
            "description": "Whether it ran.",
            "$ref": "#/components/schemas/SimulationStatus"
          },
          "source": {
            "description": "What it ran against.",
            "$ref": "#/components/schemas/SimulationSource"
          },
          "outcome": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "The verdict; set exactly when Status is SimulationStatus.Simulated.",
                "$ref": "#/components/schemas/SimulationOutcome"
              }
            ]
          },
          "upstream": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "The route's upstream, when the outcome forwards.",
                "$ref": "#/components/schemas/SimulationUpstream"
              }
            ]
          },
          "trace": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/SimulationStep"
            }
          },
          "errors": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/ConfigError"
            }
          },
          "relatedErrors": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/ConfigError"
            }
          },
          "warnings": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/ConfigWarning"
            }
          },
          "notes": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "type": "string"
            }
          }
        },
        "additionalProperties": false,
        "description": "The answer to \"what would this request get, and why\": the outcome and the ordered trace of what the real\ngate did, or, when there was nothing to simulate against, why not."
      },
      "SimulationSource": {
        "required": [
          "kind"
        ],
        "type": "object",
        "properties": {
          "kind": {
            "description": "The kind of target.",
            "$ref": "#/components/schemas/SimulationTargetKind"
          },
          "revision": {
            "examples": [
              "rev_0A1b2C3d4E5f6G7h8I9j0K"
            ],
            "pattern": "^rev_[0-9A-Za-z]{22}$",
            "type": [
              "null",
              "string"
            ],
            "description": "The revision the config is (the live one's, or the stored revision); null for a draft, and for the\n            live config before the first commit."
          }
        },
        "additionalProperties": false,
        "description": "What the simulation ran against."
      },
      "SimulationStatus": {
        "enum": [
          "simulated",
          "invalid",
          "revisionNotFound",
          "draftNotFound",
          "draftExpired",
          "draftBlocked",
          "draftRejected",
          "unavailable",
          "denied",
          "badRequest"
        ],
        "type": "string",
        "description": "Whether the simulation ran."
      },
      "SimulationStep": {
        "required": [
          "kind",
          "verdict",
          "summary",
          "facts"
        ],
        "type": "object",
        "properties": {
          "kind": {
            "description": "Which step.",
            "$ref": "#/components/schemas/SimulationStepKind"
          },
          "verdict": {
            "description": "What it did.",
            "$ref": "#/components/schemas/SimulationStepVerdict"
          },
          "summary": {
            "type": "string",
            "description": "One line saying it in words."
          },
          "facts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SimulationFact"
            },
            "description": "The details behind the line."
          },
          "host": {
            "examples": [
              "host_0A1b2C3d4E5f6G7h8I9j0K"
            ],
            "pattern": "^host_[0-9A-Za-z]{22}$",
            "type": [
              "null",
              "string"
            ],
            "description": "The host it concerns, once one matched."
          },
          "route": {
            "examples": [
              "rte_0A1b2C3d4E5f6G7h8I9j0K"
            ],
            "pattern": "^rte_[0-9A-Za-z]{22}$",
            "type": [
              "null",
              "string"
            ],
            "description": "The route it concerns, once one matched."
          },
          "policyIndex": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "description": "Policy steps: the policy's position in its chain, from 0.",
            "format": "int32"
          }
        },
        "additionalProperties": false,
        "description": "One step the gate took."
      },
      "SimulationStepKind": {
        "enum": [
          "connection",
          "tlsHandshake",
          "request",
          "clientAddress",
          "acmeChallenge",
          "hostMatch",
          "responseHeaders",
          "originLockdown",
          "clientCertificate",
          "tlsPolicy",
          "hostPolicy",
          "requestLimits",
          "httpsRedirect",
          "robotsTxt",
          "maintenance",
          "fixedResponse",
          "proofOfWork",
          "routeMatch",
          "routePolicy",
          "upstream"
        ],
        "type": "string",
        "description": "Which gate step a SimulationStep is, in the order they run."
      },
      "SimulationStepVerdict": {
        "enum": [
          "passed",
          "stopped",
          "skipped"
        ],
        "type": "string",
        "description": "What a step did to the request."
      },
      "SimulationTarget": {
        "required": [
          "kind"
        ],
        "type": "object",
        "anyOf": [
          {
            "$ref": "#/components/schemas/SimulationTargetLiveSimulationTarget"
          },
          {
            "$ref": "#/components/schemas/SimulationTargetRevisionSimulationTarget"
          },
          {
            "$ref": "#/components/schemas/SimulationTargetDraftDocumentSimulationTarget"
          },
          {
            "$ref": "#/components/schemas/SimulationTargetDraftCommandSimulationTarget"
          },
          {
            "$ref": "#/components/schemas/SimulationTargetDraftSimulationTarget"
          }
        ],
        "description": "The config to simulate against: the live snapshot, a stored revision, or a draft (the caller's stored draft, a document\nor a pending command) that is validated and compiled but never stored or published.",
        "discriminator": {
          "propertyName": "kind",
          "mapping": {
            "live": "#/components/schemas/SimulationTargetLiveSimulationTarget",
            "revision": "#/components/schemas/SimulationTargetRevisionSimulationTarget",
            "draftDocument": "#/components/schemas/SimulationTargetDraftDocumentSimulationTarget",
            "draftCommand": "#/components/schemas/SimulationTargetDraftCommandSimulationTarget",
            "draft": "#/components/schemas/SimulationTargetDraftSimulationTarget"
          }
        }
      },
      "SimulationTargetDraftCommandSimulationTarget": {
        "required": [
          "command"
        ],
        "properties": {
          "kind": {
            "enum": [
              "draftCommand"
            ],
            "type": "string"
          },
          "command": {
            "description": "The command.",
            "$ref": "#/components/schemas/ConfigCommand"
          }
        },
        "additionalProperties": false,
        "description": "A pending command, applied to the current config and validated and compiled as its dry run would. Whatever\n            bool ConfigCommand.DryRun says, nothing is stored or published."
      },
      "SimulationTargetDraftDocumentSimulationTarget": {
        "required": [
          "document"
        ],
        "properties": {
          "kind": {
            "enum": [
              "draftDocument"
            ],
            "type": "string"
          },
          "document": {
            "description": "The config to try.",
            "$ref": "#/components/schemas/ConfigDocument"
          }
        },
        "additionalProperties": false,
        "description": "A whole document. Validated and compiled like an import; its versions are not looked at."
      },
      "SimulationTargetDraftSimulationTarget": {
        "required": [
          "draft"
        ],
        "properties": {
          "kind": {
            "enum": [
              "draft"
            ],
            "type": "string"
          },
          "draft": {
            "description": "The draft.",
            "$ref": "#/components/schemas/DraftId"
          }
        },
        "additionalProperties": false,
        "description": "The caller's own stored draft, applied to the current config exactly as its review does (the same replay, the same\n            validation and compile), so it shows what applying would do. It is read at the moment of the call; nothing is stored or\n            published, and only the draft's owner (or the owner's token) can name it."
      },
      "SimulationTargetKind": {
        "enum": [
          "live",
          "revision",
          "draftDocument",
          "draftCommand",
          "draft"
        ],
        "type": "string",
        "description": "Which config the simulator runs the request against."
      },
      "SimulationTargetLiveSimulationTarget": {
        "properties": {
          "kind": {
            "enum": [
              "live"
            ],
            "type": "string"
          }
        },
        "additionalProperties": false,
        "description": "The snapshot serving traffic now."
      },
      "SimulationTargetRevisionSimulationTarget": {
        "required": [
          "revision"
        ],
        "properties": {
          "kind": {
            "enum": [
              "revision"
            ],
            "type": "string"
          },
          "revision": {
            "description": "The revision.",
            "$ref": "#/components/schemas/ConfigRevisionId"
          }
        },
        "additionalProperties": false,
        "description": "A stored revision."
      },
      "SimulationTls": {
        "type": "object",
        "properties": {
          "serverName": {
            "type": [
              "null",
              "string"
            ],
            "description": "The name in the ClientHello. Default: the URL's host when it is a DNS name; none for an IP\n            address. Set it to test a connection negotiated for one name that then asks for another (the 421 case)."
          },
          "httpVersion": {
            "enum": [
              "http11",
              "http2",
              "http3"
            ],
            "type": [
              "null",
              "string"
            ],
            "description": "Default: HTTP/2."
          },
          "tlsVersion": {
            "enum": [
              "tls12",
              "tls13"
            ],
            "type": [
              "null",
              "string"
            ],
            "description": "Default: TLS 1.3."
          }
        },
        "additionalProperties": false,
        "description": "The TLS context of an HTTPS request. Every part defaults to what a browser would do for the URL."
      },
      "SimulationUpstream": {
        "required": [
          "id",
          "destinations"
        ],
        "type": "object",
        "properties": {
          "id": {
            "description": "The upstream.",
            "$ref": "#/components/schemas/UpstreamId"
          },
          "destinations": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SimulationDestination"
            },
            "description": "The upstream's destinations in the target config, with their live health."
          },
          "loadBalancing": {
            "type": [
              "null",
              "string"
            ],
            "description": "The YARP load-balancing policy; null for YARP's default."
          }
        },
        "additionalProperties": false,
        "description": "The upstream the request is forwarded to. Load balancing is random: no destination is named as the one chosen."
      },
      "TcpAlpn": {
        "enum": [
          "http2AndHttp11",
          "http11Only"
        ],
        "type": [
          "null",
          "string"
        ]
      },
      "TlsMinimumVersion": {
        "enum": [
          "tls12",
          "tls13"
        ],
        "type": [
          "null",
          "string"
        ]
      },
      "TlsSettings": {
        "type": "object",
        "properties": {
          "minimumVersion": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Lowest TLS version over TCP (QUIC is always 1.3). Built-in default: TLS 1.2.",
                "$ref": "#/components/schemas/TlsMinimumVersion"
              }
            ]
          },
          "tcpAlpn": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "ALPN offered over TCP. Built-in default: HTTP/2 and HTTP/1.1.",
                "$ref": "#/components/schemas/TcpAlpn"
              }
            ]
          },
          "http3": {
            "type": [
              "null",
              "boolean"
            ],
            "description": "Serve over HTTP/3 when the listener exists. Built-in default: on."
          }
        },
        "additionalProperties": false,
        "description": "TLS settings; each field inherits on its own. A layer that sets none is the same as no layer."
      },
      "TokenRejections": {
        "required": [
          "malformed",
          "badTag",
          "expired",
          "tooEasy"
        ],
        "type": "object",
        "properties": {
          "malformed": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Not a token this build issues.",
            "format": "int64"
          },
          "badTag": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Altered, or issued for another host or client address.",
            "format": "int64"
          },
          "expired": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Valid, but past its lifetime.",
            "format": "int64"
          },
          "tooEasy": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Valid, but easier than the host's current difficulty.",
            "format": "int64"
          }
        },
        "additionalProperties": false,
        "description": "Why proof-of-work cookies were not accepted."
      },
      "TotpConfirmation": {
        "required": [
          "code"
        ],
        "type": "object",
        "properties": {
          "code": {
            "type": "string",
            "description": "The six digits."
          }
        },
        "additionalProperties": false,
        "description": "A code from the authenticator, to confirm an enrolment."
      },
      "TotpEnrollmentStarted": {
        "required": [
          "provisioningUri"
        ],
        "type": "object",
        "properties": {
          "provisioningUri": {
            "type": "string",
            "description": "The `otpauth://` URI for the QR code: shown once, not stored.",
            "format": "uri"
          }
        },
        "additionalProperties": false,
        "description": "The start of a TOTP enrolment."
      },
      "TrustedProxySettings": {
        "type": "object",
        "properties": {
          "mode": {
            "enum": [
              "off",
              "generic",
              "cloudflareProxy",
              "cloudflareTunnel"
            ],
            "type": "string",
            "description": "How the client IP reaches us.",
            "default": "off"
          },
          "header": {
            "type": [
              "null",
              "string"
            ],
            "description": "Generic mode only: the header carrying the client; null = `X-Forwarded-For`, read right\n            to left."
          },
          "useTrueClientIp": {
            "type": "boolean",
            "description": "Cloudflare modes: also accept `True-Client-IP` when `CF-Connecting-IP` is\n            absent.",
            "default": false
          },
          "forwardedForFallback": {
            "type": "boolean",
            "description": "Cloudflare modes: fall back to the right-most untrusted `X-Forwarded-For`\n            entry.",
            "default": false
          },
          "trustForwardedProto": {
            "type": "boolean",
            "description": "Generic mode only: take the request scheme from the trusted proxies'\n            `X-Forwarded-Proto`.",
            "default": false
          },
          "proxies": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "type": "string"
            }
          }
        },
        "additionalProperties": false,
        "description": "Trusted proxies in front of Paduraksa. Only a peer inside the trusted set may supply the\nclient IP through a header; anyone else is the client."
      },
      "UpdateUserRequest": {
        "type": "object",
        "properties": {
          "displayName": {
            "type": [
              "null",
              "string"
            ],
            "description": "The new display name."
          },
          "roles": {
            "type": [
              "null",
              "array"
            ],
            "description": "The complete new set of roles (not a delta); at least one when given."
          },
          "disabled": {
            "type": [
              "null",
              "boolean"
            ],
            "description": "True disables the user (ending their sessions; their tokens stop working), false re-enables them and clears a lockout."
          }
        },
        "additionalProperties": false,
        "description": "Changes a user; a null member stays as it is."
      },
      "UploadForm": {
        "required": [
          "password",
          "label",
          "file"
        ],
        "type": "object",
        "properties": {
          "password": {
            "type": [
              "null",
              "string"
            ]
          },
          "label": {
            "type": [
              "null",
              "string"
            ]
          },
          "file": {
            "type": "string",
            "format": "byte"
          }
        },
        "additionalProperties": false,
        "description": "The upload form, for the API description only."
      },
      "UpstreamConfig": {
        "required": [
          "id",
          "name"
        ],
        "type": "object",
        "properties": {
          "id": {
            "description": "Identity; also the YARP cluster ID.",
            "$ref": "#/components/schemas/UpstreamId"
          },
          "name": {
            "type": "string",
            "description": "Human label, unique ignoring case."
          },
          "version": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Concurrency version.",
            "format": "int64",
            "default": 0
          },
          "loadBalancing": {
            "enum": [
              "powerOfTwoChoices",
              "roundRobin",
              "random",
              "leastRequests",
              "firstAlphabetical"
            ],
            "type": [
              "null",
              "string"
            ],
            "description": "Null: whatever YARP's default is (today power of two choices); a value pins the\n            policy, so null and LoadBalancingPolicy.PowerOfTwoChoices are kept apart."
          },
          "sessionAffinity": {
            "type": "boolean",
            "description": "Cookie-based session affinity.",
            "default": false
          },
          "passiveHealth": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Passive health (transport failure rate); null = off.",
                "$ref": "#/components/schemas/PassiveHealthConfig"
              }
            ]
          },
          "activeHealth": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Active health probes; null = off.",
                "$ref": "#/components/schemas/ActiveHealthConfig"
              }
            ]
          },
          "maxConnectionsPerServer": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "description": "Null: no limit beyond the HTTP client's default.",
            "format": "int32"
          },
          "notes": {
            "type": [
              "null",
              "string"
            ],
            "description": "Free text."
          },
          "tls": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "How HTTPS destinations are authenticated; null = the system trust store.",
                "$ref": "#/components/schemas/UpstreamTlsConfig"
              }
            ]
          },
          "hostHeader": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "The `Host` the upstream receives; null = the client's own (UpstreamHostHeaderMode.Original).",
                "$ref": "#/components/schemas/UpstreamHostHeaderConfig"
              }
            ]
          },
          "timeouts": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Connect and idle timeouts; null = the built-in defaults.",
                "$ref": "#/components/schemas/UpstreamTimeoutsConfig"
              }
            ]
          },
          "destinations": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/DestinationConfig"
            }
          }
        },
        "additionalProperties": false,
        "description": "An upstream: a set of destinations that routes proxy to, compiled into a YARP cluster."
      },
      "UpstreamErrorReplacement": {
        "enum": [
          "off",
          "html",
          "any"
        ],
        "type": [
          "null",
          "string"
        ]
      },
      "UpstreamHealthState": {
        "enum": [
          "unchecked",
          "unknown",
          "healthy",
          "degraded",
          "down"
        ],
        "type": "string",
        "description": "Whether and how an upstream is serving, as the live proxy sees it."
      },
      "UpstreamHealthView": {
        "required": [
          "id",
          "state",
          "destinations"
        ],
        "type": "object",
        "properties": {
          "id": {
            "description": "The upstream.",
            "$ref": "#/components/schemas/UpstreamId"
          },
          "state": {
            "description": "The whole upstream's state.",
            "$ref": "#/components/schemas/UpstreamHealthState"
          },
          "destinations": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DestinationHealthView"
            },
            "description": "Each destination the proxy knows."
          },
          "lastChange": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "The latest change of the upstream or any of its destinations since the process started; null if none.",
                "$ref": "#/components/schemas/HealthChangeView"
              }
            ]
          }
        },
        "additionalProperties": false,
        "description": "An upstream's live health."
      },
      "UpstreamHostHeaderConfig": {
        "type": "object",
        "properties": {
          "mode": {
            "enum": [
              "original",
              "destination",
              "custom"
            ],
            "type": "string",
            "description": "Which name is sent.",
            "default": "original"
          },
          "value": {
            "type": [
              "null",
              "string"
            ],
            "description": "With UpstreamHostHeaderMode.Custom: a host name or IP address, with a port when the\n            upstream's virtual host needs one (`app.internal`, `app.internal:8080`, `[::1]:8080`). Absent otherwise."
          }
        },
        "additionalProperties": false,
        "description": "The `Host` header an upstream receives. SNI follows it on an HTTPS destination, so the certificate is asked for\n            and checked against the name sent (a pinned fingerprint is not tied to any name)."
      },
      "UpstreamId": {
        "examples": [
          "ups_0A1b2C3d4E5f6G7h8I9j0K"
        ],
        "pattern": "^ups_[0-9A-Za-z]{22}$",
        "type": "string",
        "description": "An upstream, compiled into a YARP cluster."
      },
      "UpstreamTimeoutsConfig": {
        "type": "object",
        "properties": {
          "connect": {
            "pattern": "^-?(\\d+\\.)?\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,7})?$",
            "type": [
              "null",
              "string"
            ],
            "description": "TCP and TLS connect to a destination, 100 ms to 5 minutes; null = 10 seconds."
          },
          "idle": {
            "pattern": "^-?(\\d+\\.)?\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,7})?$",
            "type": [
              "null",
              "string"
            ],
            "description": "How long a request, a response stream or an upgraded connection (a WebSocket) may go with no data in either direction before the proxy closes it, 1 second to 24 hours; zero = never; null = 1 hour."
          }
        },
        "additionalProperties": false,
        "description": "How long the proxy waits on an upstream. Each value left null is the built-in default."
      },
      "UpstreamTlsConfig": {
        "type": "object",
        "properties": {
          "trust": {
            "enum": [
              "system",
              "pinned",
              "any"
            ],
            "type": "string",
            "description": "Which certificates are accepted.",
            "default": "system"
          },
          "pinnedSha256": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "type": "string"
            },
            "description": "With UpstreamTlsTrust.Pinned: the SHA-256 fingerprints of the certificates to\n            accept, 64 hex digits each (colons allowed, as `openssl x509 -noout -fingerprint -sha256` prints them)."
          }
        },
        "additionalProperties": false,
        "description": "How the proxy authenticates an upstream's HTTPS destinations."
      },
      "UpstreamTraffic": {
        "required": [
          "id",
          "requests",
          "errors",
          "errorRate",
          "p95Ms"
        ],
        "type": "object",
        "properties": {
          "id": {
            "description": "The upstream.",
            "$ref": "#/components/schemas/UpstreamId"
          },
          "requests": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Requests forwarded to it.",
            "format": "int64"
          },
          "errors": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Forwards that failed on its side: a refused or timed-out connection, a broken response, no destination to try.",
            "format": "int64"
          },
          "errorRate": {
            "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?$",
            "type": [
              "number",
              "string"
            ],
            "description": "Errors over Requests, 0 to 1.",
            "format": "double"
          },
          "p95Ms": {
            "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?$",
            "type": [
              "number",
              "string"
            ],
            "description": "The 95th-percentile upstream time, as double HostTraffic.P95Ms.",
            "format": "double"
          }
        },
        "additionalProperties": false,
        "description": "An upstream's forwards over the range."
      },
      "UserId": {
        "examples": [
          "usr_0A1b2C3d4E5f6G7h8I9j0K"
        ],
        "pattern": "^usr_[0-9A-Za-z]{22}$",
        "type": "string",
        "description": "An admin user; its entity is `UserRow` in Data."
      },
      "UserSummary": {
        "required": [
          "id",
          "username",
          "displayName",
          "roles",
          "twoFactorEnabled",
          "disabled",
          "locked",
          "createdAt",
          "updatedAt"
        ],
        "type": "object",
        "properties": {
          "id": {
            "description": "The user.",
            "$ref": "#/components/schemas/UserId"
          },
          "username": {
            "type": "string",
            "description": "The sign-in name, as the user typed it when created."
          },
          "displayName": {
            "type": "string",
            "description": "The name shown in the UI."
          },
          "roles": {
            "type": "array",
            "description": "The roles the user holds, in a fixed order."
          },
          "twoFactorEnabled": {
            "type": "boolean",
            "description": "An authenticator is enrolled, so signing in needs a code."
          },
          "disabled": {
            "type": "boolean",
            "description": "The user cannot sign in, and their tokens and sessions are dead."
          },
          "locked": {
            "type": "boolean",
            "description": "Sign-in is refused for now after too many failures."
          },
          "createdAt": {
            "type": "string",
            "description": "When the user was created.",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "description": "When the user was last changed.",
            "format": "date-time"
          }
        },
        "additionalProperties": false,
        "description": "An admin user as the admin surfaces see them."
      },
      "VersionInfo": {
        "required": [
          "version",
          "api"
        ],
        "type": "object",
        "properties": {
          "version": {
            "type": "string",
            "description": "The product version (SemVer), for example `0.1.0`."
          },
          "api": {
            "type": "string",
            "description": "The API's major version, as in its path prefix: `v1`."
          },
          "commit": {
            "type": [
              "null",
              "string"
            ],
            "description": "The full SHA of the commit the build came from; null for a build that was not given one (the container image build passes it as `-p:SourceRevisionId`). Always written."
          }
        },
        "additionalProperties": false,
        "description": "What is running: the product's version and the commit it was built from."
      }
    },
    "securitySchemes": {
      "bearer": {
        "type": "http",
        "description": "An API token issued to a user. A signed-in session cookie is accepted as well.",
        "scheme": "bearer"
      }
    }
  },
  "tags": [
    {
      "name": "Identity"
    },
    {
      "name": "Server"
    },
    {
      "name": "Config"
    },
    {
      "name": "Settings"
    },
    {
      "name": "Certificates"
    },
    {
      "name": "Backup"
    },
    {
      "name": "Error pages"
    },
    {
      "name": "Simulator"
    },
    {
      "name": "Hosts"
    },
    {
      "name": "Upstreams"
    },
    {
      "name": "Rate limits"
    },
    {
      "name": "Access lists"
    },
    {
      "name": "Rule sets"
    },
    {
      "name": "Auth providers"
    },
    {
      "name": "Profiles"
    },
    {
      "name": "Drafts"
    },
    {
      "name": "Revisions"
    },
    {
      "name": "ACME accounts"
    },
    {
      "name": "DNS providers"
    },
    {
      "name": "Notifications"
    },
    {
      "name": "Audit"
    },
    {
      "name": "Logs"
    },
    {
      "name": "Security"
    },
    {
      "name": "Metrics"
    },
    {
      "name": "Analytics"
    },
    {
      "name": "CA"
    }
  ]
}