{
  "openapi": "3.1.0",
  "info": {
    "title": "Suede",
    "description": "Remote management for Sway-based display appliances. Clients write desired state; a reconciler drives the live session toward it.",
    "contact": {
      "name": "Hamish Barjonas"
    },
    "license": {
      "name": "PolyForm-Small-Business-1.0.0",
      "url": "https://github.com/gameshowpro/Suede/blob/main/LICENSE"
    },
    "version": "0.1.14"
  },
  "servers": [
    {
      "url": "http://{host}:{port}",
      "description": "A Suede appliance",
      "variables": {
        "host": {
          "default": "127.0.0.1",
          "description": "Hostname or address of the appliance"
        },
        "port": {
          "default": "9088",
          "description": "The API port"
        }
      }
    }
  ],
  "paths": {
    "/api/v1/apps": {
      "get": {
        "tags": [
          "apps"
        ],
        "operationId": "list_apps",
        "responses": {
          "200": {
            "description": "Status of every managed app",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/AppStatus"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/apps/capabilities": {
      "post": {
        "tags": [
          "apps"
        ],
        "operationId": "run_capability_check",
        "parameters": [
          {
            "name": "timeoutSeconds",
            "in": "query",
            "description": "How long to wait for the page's report. Clamped to 5–120, default 30.",
            "required": false,
            "schema": {
              "type": [
                "integer",
                "null"
              ],
              "format": "int64",
              "minimum": 0
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AppConfig"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The check ran; `completed` says whether the page reported before the browser was taken down. A window opens on the appliance's displays while it runs.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CapabilityOutcome"
                }
              }
            }
          },
          "400": {
            "description": "The launcher is not a browser, or no browser is installed"
          },
          "409": {
            "description": "A capability check is already running"
          }
        }
      }
    },
    "/api/v1/apps/capabilities/last": {
      "get": {
        "tags": [
          "apps"
        ],
        "operationId": "get_last",
        "responses": {
          "200": {
            "description": "The most recent capability measurement",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LastMeasurement"
                }
              }
            }
          },
          "404": {
            "description": "Nothing has been measured yet"
          }
        }
      }
    },
    "/api/v1/apps/capabilities/measure": {
      "post": {
        "tags": [
          "apps"
        ],
        "summary": "The trigger a health-check alert can offer: no body to construct, the\nsubject is whatever this appliance is configured to run — the same choice\nthe startup measurement makes.",
        "operationId": "measure_subject",
        "parameters": [
          {
            "name": "timeoutSeconds",
            "in": "query",
            "description": "How long to wait for the page's report. Clamped to 5–120, default 30.",
            "required": false,
            "schema": {
              "type": [
                "integer",
                "null"
              ],
              "format": "int64",
              "minimum": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Measured the configured application — the active browser app, or the first one. A window opens on the appliance's displays while it runs.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CapabilityOutcome"
                }
              }
            }
          },
          "400": {
            "description": "No browser application is configured"
          },
          "409": {
            "description": "A capability check is already running"
          }
        }
      }
    },
    "/api/v1/apps/preview": {
      "post": {
        "tags": [
          "apps"
        ],
        "operationId": "preview_app",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AppConfig"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "What this application would be launched as, resolved                        against this machine. Nothing is started.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LaunchPreview"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/apps/{id}/activate": {
      "post": {
        "tags": [
          "apps"
        ],
        "operationId": "activate_app",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "App identifier",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "wait",
            "in": "query",
            "description": "Block until reconciliation settles, or this many seconds elapse.",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int64",
              "minimum": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The persisted document",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DesiredState"
                }
              }
            }
          },
          "404": {
            "description": "No such app"
          }
        }
      }
    },
    "/api/v1/apps/{id}/deactivate": {
      "post": {
        "tags": [
          "apps"
        ],
        "operationId": "deactivate_app",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "App identifier",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "wait",
            "in": "query",
            "description": "Block until reconciliation settles, or this many seconds elapse.",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int64",
              "minimum": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The persisted document",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DesiredState"
                }
              }
            }
          },
          "404": {
            "description": "No such app"
          },
          "409": {
            "description": "A different app is active"
          }
        }
      }
    },
    "/api/v1/apps/{id}/heartbeat": {
      "post": {
        "tags": [
          "apps"
        ],
        "operationId": "heartbeat",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "App identifier",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Heartbeat recorded"
          },
          "403": {
            "description": "Only loopback callers may post heartbeats"
          },
          "404": {
            "description": "No such app"
          }
        }
      }
    },
    "/api/v1/apps/{id}/restart": {
      "post": {
        "tags": [
          "apps"
        ],
        "operationId": "restart_app",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "App identifier",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Status after the restart",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AppStatus"
                }
              }
            }
          },
          "404": {
            "description": "No such app"
          }
        }
      }
    },
    "/api/v1/apps/{id}/status": {
      "get": {
        "tags": [
          "apps"
        ],
        "operationId": "get_app_status",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "App identifier",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Runtime status",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AppStatus"
                }
              }
            }
          },
          "404": {
            "description": "No such app"
          }
        }
      }
    },
    "/api/v1/av": {
      "get": {
        "tags": [
          "observed"
        ],
        "operationId": "list_av_devices",
        "responses": {
          "200": {
            "description": "Every AV device PipeWire reports, in three lists: audio outputs (sinks Suede can route to), audio inputs, and video inputs. Only audio outputs have a default. Exposed so an app in the kiosk browser, which cannot enumerate devices itself, can ask for the right one: a browser labels an audio device by its `description` and a video device by its `card`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AvDevices"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/capability-check/{id}/result": {
      "post": {
        "tags": [
          "apps"
        ],
        "operationId": "post_result",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The check id from the page's own URL",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CapabilityReport"
              }
            }
          },
          "required": true
        },
        "responses": {
          "204": {
            "description": "Report accepted"
          },
          "403": {
            "description": "Only the local machine may report"
          },
          "404": {
            "description": "No check with that id is waiting"
          }
        }
      }
    },
    "/api/v1/config": {
      "get": {
        "tags": [
          "config"
        ],
        "operationId": "get_config",
        "responses": {
          "200": {
            "description": "The current document. `committed: false` means a working copy is live on the outputs but not saved.",
            "headers": {
              "ETag": {
                "schema": {
                  "type": "string"
                },
                "description": "Persisted document revision, quoted for If-Match"
              },
              "X-Config-Epoch": {
                "schema": {
                  "type": "string"
                },
                "description": "Store instance identity for If-Config-Epoch"
              },
              "X-Config-Generation": {
                "schema": {
                  "type": "integer",
                  "format": "int64",
                  "minimum": 0
                },
                "description": "Effective working-copy generation"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DesiredState"
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "config"
        ],
        "operationId": "put_config",
        "parameters": [
          {
            "name": "wait",
            "in": "query",
            "description": "Block until reconciliation settles, or this many seconds elapse.",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int64",
              "minimum": 0
            }
          },
          {
            "name": "If-Match",
            "in": "header",
            "description": "Optional persisted revision precondition from ETag",
            "required": false,
            "schema": {
              "type": [
                "string",
                "null"
              ]
            }
          },
          {
            "name": "If-Config-Generation",
            "in": "header",
            "description": "Optional working-copy generation precondition",
            "required": false,
            "schema": {
              "type": [
                "integer",
                "null"
              ],
              "format": "int64",
              "minimum": 0
            }
          },
          {
            "name": "If-Config-Epoch",
            "in": "header",
            "description": "Optional store-instance precondition from X-Config-Epoch",
            "required": false,
            "schema": {
              "type": [
                "string",
                "null"
              ]
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DesiredState"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The accepted document",
            "headers": {
              "ETag": {
                "schema": {
                  "type": "string"
                },
                "description": "Persisted document revision, quoted for If-Match"
              },
              "X-Config-Epoch": {
                "schema": {
                  "type": "string"
                },
                "description": "Store instance identity for If-Config-Epoch"
              },
              "X-Config-Generation": {
                "schema": {
                  "type": "integer",
                  "format": "int64",
                  "minimum": 0
                },
                "description": "Effective working-copy generation"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DesiredState"
                }
              }
            }
          },
          "409": {
            "description": "If-Match, If-Config-Generation, or If-Config-Epoch is stale"
          },
          "422": {
            "description": "Validation failed"
          }
        }
      }
    },
    "/api/v1/config/apps": {
      "get": {
        "tags": [
          "config"
        ],
        "operationId": "get_apps",
        "responses": {
          "200": {
            "description": "Configured apps, from the effective document",
            "headers": {
              "ETag": {
                "schema": {
                  "type": "string"
                },
                "description": "Persisted document revision, quoted for If-Match"
              },
              "X-Config-Epoch": {
                "schema": {
                  "type": "string"
                },
                "description": "Store instance identity for If-Config-Epoch"
              },
              "X-Config-Generation": {
                "schema": {
                  "type": "integer",
                  "format": "int64",
                  "minimum": 0
                },
                "description": "Effective working-copy generation"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/AppConfig"
                  }
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "config"
        ],
        "operationId": "put_apps",
        "parameters": [
          {
            "name": "wait",
            "in": "query",
            "description": "Block until reconciliation settles, or this many seconds elapse.",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int64",
              "minimum": 0
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/AppConfig"
                }
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The persisted document",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DesiredState"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/config/apps/{id}": {
      "get": {
        "tags": [
          "config"
        ],
        "operationId": "get_app",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "App identifier",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The app configuration, from the effective document",
            "headers": {
              "ETag": {
                "schema": {
                  "type": "string"
                },
                "description": "Persisted document revision, quoted for If-Match"
              },
              "X-Config-Epoch": {
                "schema": {
                  "type": "string"
                },
                "description": "Store instance identity for If-Config-Epoch"
              },
              "X-Config-Generation": {
                "schema": {
                  "type": "integer",
                  "format": "int64",
                  "minimum": 0
                },
                "description": "Effective working-copy generation"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AppConfig"
                }
              }
            }
          },
          "404": {
            "description": "No such app"
          }
        }
      },
      "put": {
        "tags": [
          "config"
        ],
        "operationId": "put_app",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "App identifier",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "wait",
            "in": "query",
            "description": "Block until reconciliation settles, or this many seconds elapse.",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int64",
              "minimum": 0
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AppConfig"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The persisted document",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DesiredState"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "config"
        ],
        "operationId": "delete_app",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "App identifier",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "wait",
            "in": "query",
            "description": "Block until reconciliation settles, or this many seconds elapse.",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int64",
              "minimum": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The persisted document",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DesiredState"
                }
              }
            }
          },
          "404": {
            "description": "No such app"
          }
        }
      }
    },
    "/api/v1/config/backgrounds": {
      "get": {
        "tags": [
          "config"
        ],
        "operationId": "get_backgrounds",
        "responses": {
          "200": {
            "description": "Defined background presets, from the effective document",
            "headers": {
              "ETag": {
                "schema": {
                  "type": "string"
                },
                "description": "Persisted document revision, quoted for If-Match"
              },
              "X-Config-Epoch": {
                "schema": {
                  "type": "string"
                },
                "description": "Store instance identity for If-Config-Epoch"
              },
              "X-Config-Generation": {
                "schema": {
                  "type": "integer",
                  "format": "int64",
                  "minimum": 0
                },
                "description": "Effective working-copy generation"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/BackgroundPreset"
                  }
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "config"
        ],
        "operationId": "put_backgrounds",
        "parameters": [
          {
            "name": "wait",
            "in": "query",
            "description": "Block until reconciliation settles, or this many seconds elapse.",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int64",
              "minimum": 0
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/BackgroundPreset"
                }
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The persisted document",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DesiredState"
                }
              }
            }
          },
          "422": {
            "description": "A preset is invalid, or an output refers to one that is gone"
          }
        }
      }
    },
    "/api/v1/config/backgrounds/{id}": {
      "put": {
        "tags": [
          "config"
        ],
        "operationId": "put_background",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Preset id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "wait",
            "in": "query",
            "description": "Block until reconciliation settles, or this many seconds elapse.",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int64",
              "minimum": 0
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BackgroundPreset"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The persisted document",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DesiredState"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "config"
        ],
        "operationId": "delete_background",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Preset id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "wait",
            "in": "query",
            "description": "Block until reconciliation settles, or this many seconds elapse.",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int64",
              "minimum": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The persisted document",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DesiredState"
                }
              }
            }
          },
          "404": {
            "description": "No such preset"
          },
          "409": {
            "description": "An output still uses this preset"
          }
        }
      }
    },
    "/api/v1/config/outputs": {
      "get": {
        "tags": [
          "config"
        ],
        "operationId": "get_outputs",
        "responses": {
          "200": {
            "description": "Configured outputs, from the effective document",
            "headers": {
              "ETag": {
                "schema": {
                  "type": "string"
                },
                "description": "Persisted document revision, quoted for If-Match"
              },
              "X-Config-Epoch": {
                "schema": {
                  "type": "string"
                },
                "description": "Store instance identity for If-Config-Epoch"
              },
              "X-Config-Generation": {
                "schema": {
                  "type": "integer",
                  "format": "int64",
                  "minimum": 0
                },
                "description": "Effective working-copy generation"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/OutputConfig"
                  }
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "config"
        ],
        "operationId": "put_outputs",
        "parameters": [
          {
            "name": "wait",
            "in": "query",
            "description": "Block until reconciliation settles, or this many seconds elapse.",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int64",
              "minimum": 0
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/OutputConfig"
                }
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The persisted document",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DesiredState"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/config/outputs/{key}": {
      "get": {
        "tags": [
          "config"
        ],
        "operationId": "get_output",
        "parameters": [
          {
            "name": "key",
            "in": "path",
            "description": "Match key, e.g. HDMI-A-1",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The output configuration, from the effective document",
            "headers": {
              "ETag": {
                "schema": {
                  "type": "string"
                },
                "description": "Persisted document revision, quoted for If-Match"
              },
              "X-Config-Epoch": {
                "schema": {
                  "type": "string"
                },
                "description": "Store instance identity for If-Config-Epoch"
              },
              "X-Config-Generation": {
                "schema": {
                  "type": "integer",
                  "format": "int64",
                  "minimum": 0
                },
                "description": "Effective working-copy generation"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OutputConfig"
                }
              }
            }
          },
          "404": {
            "description": "No such entry"
          }
        }
      },
      "put": {
        "tags": [
          "config"
        ],
        "operationId": "put_output",
        "parameters": [
          {
            "name": "key",
            "in": "path",
            "description": "Match key",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "wait",
            "in": "query",
            "description": "Block until reconciliation settles, or this many seconds elapse.",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int64",
              "minimum": 0
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OutputConfig"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The persisted document. Retention note: while the effective projection mode is Simple, omitting `geometry` here (or leaving it out of a full PUT /config) keeps the previously saved geometry rather than clearing it, so a Simple-mode save cannot accidentally discard retained Warp calibration. PUT is otherwise literal. To actually clear one output's geometry, use DELETE on this same path with an additional /geometry segment.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DesiredState"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "config"
        ],
        "operationId": "delete_output",
        "parameters": [
          {
            "name": "key",
            "in": "path",
            "description": "Match key",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "wait",
            "in": "query",
            "description": "Block until reconciliation settles, or this many seconds elapse.",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int64",
              "minimum": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The persisted document",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DesiredState"
                }
              }
            }
          },
          "404": {
            "description": "No such entry"
          }
        }
      }
    },
    "/api/v1/config/outputs/{key}/geometry": {
      "delete": {
        "tags": [
          "config"
        ],
        "summary": "Explicit calibration clear. A full-document or section PUT cannot do\nthis while the effective mode is Simple: `preserve_retained` treats an\nomitted `geometry` as \"leave it\", precisely so an ordinary Simple-mode\nsave cannot discard retained Warp correction by accident (see\n[`crate::projection_policy::preserve_retained`]). This route bypasses\nthat retention deliberately and is the only way to clear one output's\ngeometry. Idempotent: a second call on an output that already has no\ngeometry still succeeds, since the end state (no geometry) is what was\nasked for either way.",
        "operationId": "delete_output_geometry",
        "parameters": [
          {
            "name": "key",
            "in": "path",
            "description": "Match key",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "wait",
            "in": "query",
            "description": "Block until reconciliation settles, or this many seconds elapse.",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int64",
              "minimum": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The persisted document, with this output's geometry cleared",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DesiredState"
                }
              }
            }
          },
          "404": {
            "description": "No such output"
          }
        }
      }
    },
    "/api/v1/config/projection": {
      "get": {
        "tags": [
          "config"
        ],
        "operationId": "get_projection",
        "responses": {
          "200": {
            "description": "The projection configuration from the effective document; null when none is set",
            "headers": {
              "ETag": {
                "schema": {
                  "type": "string"
                },
                "description": "Persisted document revision, quoted for If-Match"
              },
              "X-Config-Epoch": {
                "schema": {
                  "type": "string"
                },
                "description": "Store instance identity for If-Config-Epoch"
              },
              "X-Config-Generation": {
                "schema": {
                  "type": "integer",
                  "format": "int64",
                  "minimum": 0
                },
                "description": "Effective working-copy generation"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "null"
                    },
                    {
                      "$ref": "#/components/schemas/ProjectionConfig"
                    }
                  ]
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "config"
        ],
        "operationId": "put_projection",
        "parameters": [
          {
            "name": "wait",
            "in": "query",
            "description": "Block until reconciliation settles, or this many seconds elapse.",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int64",
              "minimum": 0
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "oneOf": [
                  {
                    "type": "null"
                  },
                  {
                    "$ref": "#/components/schemas/ProjectionConfig"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The persisted document. `null` removes the whole projection section. Retention note: while the effective mode is Simple, an otherwise-present projection body that omits `canvas` keeps the previously saved canvas rather than clearing it, so a Simple-mode save cannot accidentally discard a retained Warp canvas. PUT is otherwise literal. To actually clear the shared canvas, use DELETE /config/projection/canvas.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DesiredState"
                }
              }
            }
          },
          "422": {
            "description": "Validation failed"
          }
        }
      }
    },
    "/api/v1/config/projection/arrangement": {
      "get": {
        "tags": [
          "config"
        ],
        "operationId": "get_projection_arrangement",
        "responses": {
          "200": {
            "description": "The persisted grid-arrangement record from the effective document, whether re-solving it still reproduces the current slices, and (when computable) the overlap limits at the recorded values",
            "headers": {
              "ETag": {
                "schema": {
                  "type": "string"
                },
                "description": "Persisted document revision, quoted for If-Match"
              },
              "X-Config-Epoch": {
                "schema": {
                  "type": "string"
                },
                "description": "Store instance identity for If-Config-Epoch"
              },
              "X-Config-Generation": {
                "schema": {
                  "type": "integer",
                  "format": "int64",
                  "minimum": 0
                },
                "description": "Effective working-copy generation"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ArrangementStatus"
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "config"
        ],
        "summary": "Solve a grid arrangement and write it — each enabled output's\n`geometry.slice`, and the resolved five values at\n`projection.arrangement` — exactly like any other config write:\n`committed: false` (the default) replaces the shared working copy and\napplies to the outputs; `committed: true` persists it. See\n[`crate::model::arrangement`] for the solver itself and\n`docs/configuration.md`'s \"Grid arrangement\" section for the contract.",
        "operationId": "put_projection_arrangement",
        "parameters": [
          {
            "name": "wait",
            "in": "query",
            "description": "Block until reconciliation settles, or this many seconds elapse.",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int64",
              "minimum": 0
            }
          },
          {
            "name": "If-Match",
            "in": "header",
            "description": "Optional persisted revision precondition from ETag",
            "required": false,
            "schema": {
              "type": [
                "string",
                "null"
              ]
            }
          },
          {
            "name": "If-Config-Generation",
            "in": "header",
            "description": "Optional working-copy generation precondition",
            "required": false,
            "schema": {
              "type": [
                "integer",
                "null"
              ],
              "format": "int64",
              "minimum": 0
            }
          },
          {
            "name": "If-Config-Epoch",
            "in": "header",
            "description": "Optional store-instance precondition from X-Config-Epoch",
            "required": false,
            "schema": {
              "type": [
                "string",
                "null"
              ]
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ArrangementRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The accepted document; the resolved five values are at `projection.arrangement` and every enabled output's `geometry.slice` reflects the solve",
            "headers": {
              "ETag": {
                "schema": {
                  "type": "string"
                },
                "description": "Persisted document revision, quoted for If-Match"
              },
              "X-Config-Epoch": {
                "schema": {
                  "type": "string"
                },
                "description": "Store instance identity for If-Config-Epoch"
              },
              "X-Config-Generation": {
                "schema": {
                  "type": "integer",
                  "format": "int64",
                  "minimum": 0
                },
                "description": "Effective working-copy generation"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DesiredState"
                }
              }
            }
          },
          "409": {
            "description": "If-Match, If-Config-Generation, or If-Config-Epoch is stale"
          },
          "422": {
            "description": "The grid does not fit the outputs, an output has no known raster, the request is over- or under-determined, an overlap is outside [0, 1), an edge slice would fall entirely outside the canvas, or the result fails document validation"
          }
        }
      }
    },
    "/api/v1/config/projection/canvas": {
      "delete": {
        "tags": [
          "config"
        ],
        "summary": "Explicit canvas clear, for the same reason `DELETE\n/config/outputs/{key}/geometry` exists: `preserve_retained` deliberately\nrefills an omitted `canvas` while the effective mode is Simple, so\nordinary section and full-document PUTs cannot clear it. A document with\nno `projection` section at all, or one whose canvas is already absent,\nis left as it is — clearing an already-clear canvas is still success.",
        "operationId": "delete_projection_canvas",
        "parameters": [
          {
            "name": "wait",
            "in": "query",
            "description": "Block until reconciliation settles, or this many seconds elapse.",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int64",
              "minimum": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The persisted document, with the shared canvas cleared",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DesiredState"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/config/revert": {
      "post": {
        "tags": [
          "config"
        ],
        "operationId": "revert_config",
        "parameters": [
          {
            "name": "If-Match",
            "in": "header",
            "description": "Optional persisted revision precondition from ETag",
            "required": false,
            "schema": {
              "type": [
                "string",
                "null"
              ]
            }
          },
          {
            "name": "If-Config-Generation",
            "in": "header",
            "description": "Optional working-copy generation precondition",
            "required": false,
            "schema": {
              "type": [
                "integer",
                "null"
              ],
              "format": "int64",
              "minimum": 0
            }
          },
          {
            "name": "If-Config-Epoch",
            "in": "header",
            "description": "Optional store-instance precondition from X-Config-Epoch",
            "required": false,
            "schema": {
              "type": [
                "string",
                "null"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Working copy discarded; the saved document is re-applied and returned",
            "headers": {
              "ETag": {
                "schema": {
                  "type": "string"
                },
                "description": "Persisted document revision, quoted for If-Match"
              },
              "X-Config-Epoch": {
                "schema": {
                  "type": "string"
                },
                "description": "Store instance identity for If-Config-Epoch"
              },
              "X-Config-Generation": {
                "schema": {
                  "type": "integer",
                  "format": "int64",
                  "minimum": 0
                },
                "description": "Effective working-copy generation"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DesiredState"
                }
              }
            }
          },
          "409": {
            "description": "If-Match, If-Config-Generation, or If-Config-Epoch is stale"
          }
        }
      }
    },
    "/api/v1/config/settings": {
      "get": {
        "tags": [
          "config"
        ],
        "operationId": "get_settings",
        "responses": {
          "200": {
            "description": "Daemon settings, from the effective document",
            "headers": {
              "ETag": {
                "schema": {
                  "type": "string"
                },
                "description": "Persisted document revision, quoted for If-Match"
              },
              "X-Config-Epoch": {
                "schema": {
                  "type": "string"
                },
                "description": "Store instance identity for If-Config-Epoch"
              },
              "X-Config-Generation": {
                "schema": {
                  "type": "integer",
                  "format": "int64",
                  "minimum": 0
                },
                "description": "Effective working-copy generation"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Settings"
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "config"
        ],
        "operationId": "put_settings",
        "parameters": [
          {
            "name": "wait",
            "in": "query",
            "description": "Block until reconciliation settles, or this many seconds elapse.",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int64",
              "minimum": 0
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Settings"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The persisted document",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DesiredState"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/events": {
      "get": {
        "tags": [
          "events"
        ],
        "operationId": "stream",
        "responses": {
          "200": {
            "description": "Stream of named server-sent events",
            "content": {
              "text/event-stream": {}
            }
          }
        }
      }
    },
    "/api/v1/outputs": {
      "get": {
        "tags": [
          "observed"
        ],
        "operationId": "list_outputs",
        "responses": {
          "200": {
            "description": "Outputs reported by sway",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Output"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/outputs/{name}": {
      "get": {
        "tags": [
          "observed"
        ],
        "operationId": "get_output",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "description": "Connector name, e.g. HDMI-A-1",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The output",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Output"
                }
              }
            }
          },
          "404": {
            "description": "No such output"
          }
        }
      }
    },
    "/api/v1/ports": {
      "get": {
        "tags": [
          "observed"
        ],
        "operationId": "list_ports",
        "responses": {
          "200": {
            "description": "Every connector on the graphics hardware, attached or not. Offered so a client can let the operator configure a socket before its display arrives; sway remains the authority on what is actually driving a display.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Port"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/projection/arrangement": {
      "get": {
        "tags": [
          "projection"
        ],
        "summary": "`GET /api/v1/projection/arrangement` — solve a grid arrangement without\nwriting it, exactly like [`recommend_resolution`]: reads the effective\ndocument and changes nothing. Pure arithmetic over a handful of outputs,\nso unlike the recommendation endpoint this needs no `spawn_blocking` —\nthe limits scan included, which re-runs the same placement a few hundred\ntimes at most.",
        "operationId": "get_arrangement",
        "parameters": [
          {
            "name": "rows",
            "in": "query",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32",
              "minimum": 0
            }
          },
          {
            "name": "columns",
            "in": "query",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32",
              "minimum": 0
            }
          },
          {
            "name": "overlapX",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "format": "double"
            }
          },
          {
            "name": "overlapY",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "format": "double"
            }
          },
          {
            "name": "contentScale",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "format": "double"
            }
          },
          {
            "name": "allowUnusedCanvas",
            "in": "query",
            "description": "As [`ArrangementRequest::allow_unused_canvas`]: default `false` means\noverlaps cover the whole canvas, overhanging it where they must, and\na content-scale band is a `422` here too, not just on the `PUT` that\nwould write it — a client previewing a rearrangement sees the same\nanswer or refusal it would get from committing it. `true` fits the\ngrid inside the canvas instead and reports the band.",
            "required": false,
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The solved arrangement; nothing is written",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ArrangementDryRun"
                }
              }
            }
          },
          "422": {
            "description": "The grid does not fit the outputs, an output has no known raster, the canvas is missing, the request is over- or under-determined, an overlap is outside [0, 1), or an edge slice would fall entirely outside the canvas"
          }
        }
      }
    },
    "/api/v1/projection/recommendation": {
      "get": {
        "tags": [
          "projection"
        ],
        "summary": "`GET /api/v1/projection/recommendation` — estimate a useful canvas density.",
        "operationId": "recommend_resolution",
        "responses": {
          "200": {
            "description": "Approximate render-resolution guidance",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RecommendationResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/projection/stats": {
      "get": {
        "tags": [
          "observed"
        ],
        "operationId": "get_projection_stats",
        "responses": {
          "200": {
            "description": "What the projection pipeline is doing right now: whether a slicer is alive, and what it measured over its last reporting interval, if any has completed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProjectionReport"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/reconcile": {
      "post": {
        "tags": [
          "control"
        ],
        "operationId": "reconcile_now",
        "responses": {
          "200": {
            "description": "Status after the pass",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Status"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/status": {
      "get": {
        "tags": [
          "observed"
        ],
        "operationId": "get_status",
        "responses": {
          "200": {
            "description": "Reconciliation status",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Status"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/sway/command": {
      "post": {
        "tags": [
          "control"
        ],
        "operationId": "run_sway_command",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SwayCommand"
              }
            }
          },
          "required": true
        },
        "responses": {
          "204": {
            "description": "Sway accepted the command"
          },
          "503": {
            "description": "Sway rejected the command"
          }
        }
      }
    },
    "/api/v1/system": {
      "get": {
        "tags": [
          "observed"
        ],
        "operationId": "get_system",
        "responses": {
          "200": {
            "description": "Daemon and environment information",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SystemInfo"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/system/checks": {
      "get": {
        "tags": [
          "observed"
        ],
        "operationId": "list_checks",
        "responses": {
          "200": {
            "description": "Environment health checks",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Check"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/system/checks/{id}/fix": {
      "post": {
        "tags": [
          "observed"
        ],
        "operationId": "fix_check",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Check identifier",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "What the fix did",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FixOutcome"
                }
              }
            }
          },
          "404": {
            "description": "No automated fix exists for this check"
          }
        }
      }
    },
    "/api/v1/system/power": {
      "post": {
        "tags": [
          "control"
        ],
        "operationId": "power",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PowerRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "202": {
            "description": "The instruction was accepted. Whether it completes is no longer observable over this connection.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PowerOutcome"
                }
              }
            }
          },
          "400": {
            "description": "confirm does not repeat this machine's hostname"
          },
          "403": {
            "description": "This verb is not permitted by bootstrap.power"
          },
          "503": {
            "description": "systemctl refused, or could not be run"
          }
        }
      }
    },
    "/api/v1/wallpapers": {
      "get": {
        "tags": [
          "wallpapers"
        ],
        "operationId": "list",
        "responses": {
          "200": {
            "description": "Stored wallpapers",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Wallpaper"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/wallpapers/{id}": {
      "get": {
        "tags": [
          "wallpapers"
        ],
        "operationId": "download",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Wallpaper identifier",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The image itself",
            "content": {
              "image/png": {}
            }
          },
          "404": {
            "description": "No such wallpaper"
          }
        }
      },
      "put": {
        "tags": [
          "wallpapers"
        ],
        "operationId": "upload",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Wallpaper identifier",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "description": "Raw PNG or JPEG image",
          "content": {
            "application/octet-stream": {
              "schema": {
                "type": "array",
                "items": {
                  "type": "integer",
                  "format": "int32",
                  "minimum": 0
                }
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The stored wallpaper",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Wallpaper"
                }
              }
            }
          },
          "422": {
            "description": "Not a PNG or JPEG, or too large"
          }
        }
      },
      "delete": {
        "tags": [
          "wallpapers"
        ],
        "operationId": "delete",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Wallpaper identifier",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Removed"
          },
          "404": {
            "description": "No such wallpaper"
          },
          "409": {
            "description": "Still referenced by an output"
          }
        }
      }
    },
    "/api/v1/windows": {
      "get": {
        "tags": [
          "observed"
        ],
        "operationId": "list_windows",
        "responses": {
          "200": {
            "description": "Windows in sway's tree",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Window"
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "ActiveApp": {
        "type": "object",
        "description": "What `Status.activeApp` says about the one app the appliance is showing.",
        "required": [
          "id",
          "state"
        ],
        "properties": {
          "detail": {
            "type": [
              "string",
              "null"
            ],
            "description": "Human-readable detail for the current state, mirroring\n[`AppStatus::detail`]."
          },
          "id": {
            "type": "string"
          },
          "state": {
            "$ref": "#/components/schemas/AppState"
          }
        }
      },
      "AdoptedOutput": {
        "type": "object",
        "description": "What Suede observed and pinned because the configuration named none.\n\nKept apart from the operator's own fields, not merged into them, because\nthe two must stay distinguishable forever: an operator who asked for\n1920x1200 and cannot have it deserves a divergence that persists until a\nhuman decides, while a value that is merely what happened to be plugged in\nlast week should be replaced without ceremony when the display changes.\nMerged into one field, nothing could tell those two cases apart.\n\nDeliberately absent: **position**. In projection mode the configured\nposition is a canvas coordinate where the beams overlap, while sway is\nhanded a plain edge-to-edge tiling, so observed position is not desired\nposition by design — on the four-projector bench the configuration holds\na 2x2 grid while sway reports a single row, and adopting observed\npositions would flatten the layout and destroy the blend. Do not add it.",
        "properties": {
          "capturedAt": {
            "type": "integer",
            "format": "int64",
            "description": "Unix seconds, so an operator can see how old a pin is.",
            "minimum": 0
          },
          "display": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/DisplayIdentity",
                "description": "The display these were taken from, so that swapping the display on a\nconnector re-adopts rather than pinning the old one's values forever.\n`None` when the display reported no EDID identity at all."
              }
            ]
          },
          "mode": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/Mode"
              }
            ]
          },
          "scale": {
            "type": [
              "number",
              "null"
            ],
            "format": "double"
          },
          "transform": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/Transform"
              }
            ]
          }
        },
        "additionalProperties": false
      },
      "AppConfig": {
        "type": "object",
        "description": "A managed application: a launch specification, not a window.",
        "required": [
          "id",
          "launcher"
        ],
        "properties": {
          "audio": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/AudioConfig",
                "description": "Audio routing. Absent leaves routing untouched; `{\"output\": null}` silences."
              }
            ]
          },
          "env": {
            "type": "object",
            "description": "Extra environment variables for the launched process.\n\nApplied last, so they override anything the launcher preset sets.\nHardware acceleration usually needs this: enabling NVDEC on an Nvidia\ncard, for instance, is a matter of `LIBVA_DRIVER_NAME` and\n`NVD_BACKEND` rather than any command-line flag.",
            "additionalProperties": {
              "type": "string"
            },
            "propertyNames": {
              "type": "string"
            }
          },
          "heartbeat": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/HeartbeatConfig"
              }
            ]
          },
          "id": {
            "type": "string",
            "description": "Client-chosen, unique, stable identifier."
          },
          "launcher": {
            "$ref": "#/components/schemas/Launcher"
          },
          "persistProfile": {
            "type": "boolean",
            "description": "Keep the browser profile between launches instead of wiping it."
          },
          "readiness": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/ReadinessConfig",
                "description": "Wait for this URL to answer before launching."
              }
            ]
          },
          "restart": {
            "$ref": "#/components/schemas/RestartPolicy"
          }
        },
        "additionalProperties": false
      },
      "AppState": {
        "type": "string",
        "description": "Lifecycle state of a supervised application.\n\nValues are camelCase like the rest of the API: `lowercase` would render\n`WaitingForOutput` as the unreadable `waitingforoutput`.",
        "enum": [
          "starting",
          "running",
          "stopped",
          "crashed",
          "backoff",
          "waitingForOutput",
          "waitingForDependency"
        ]
      },
      "AppStatus": {
        "type": "object",
        "description": "Runtime status of a supervised application.",
        "required": [
          "id",
          "state",
          "restartCount",
          "windowIds"
        ],
        "properties": {
          "detail": {
            "type": [
              "string",
              "null"
            ],
            "description": "Human-readable detail for the current state."
          },
          "id": {
            "type": "string"
          },
          "lastExitCode": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int32"
          },
          "lastHeartbeat": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "description": "Unix seconds of the most recent heartbeat, when the watchdog is enabled.",
            "minimum": 0
          },
          "lastRestartReason": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/RestartReason"
              }
            ]
          },
          "pid": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int32",
            "minimum": 0
          },
          "restartCount": {
            "type": "integer",
            "format": "int32",
            "minimum": 0
          },
          "startedAt": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "description": "Unix seconds when the current process was spawned.",
            "minimum": 0
          },
          "state": {
            "$ref": "#/components/schemas/AppState"
          },
          "windowIds": {
            "type": "array",
            "items": {
              "type": "integer",
              "format": "int64"
            },
            "description": "Sway container ids currently attributed to this app."
          }
        }
      },
      "ArrangeOffset": {
        "type": "object",
        "description": "A per-output nudge the grid arrangement solve adds to this output's\ncomputed slice, in normalized canvas units — the same space as\n[`super::geometry::OutputGeometry::slice`].\n\nApplied only by [`crate::model::arrangement::solve`], after placement:\ncoverage (`unusedCanvas`) and the strict full-coverage gate are computed\non the *pre-offset* rectangles, since an offset is a deliberate shift\n(mechanical alignment, for instance) that may uncover canvas pixels on\npurpose rather than a coverage failure. [`crate::model::arrangement::in_effect`]\napplies the document's current offset before comparing, so editing the\noffset after an arrangement has been applied turns `inEffect` false, like\nany other manual geometry edit.",
        "required": [
          "x",
          "y"
        ],
        "properties": {
          "x": {
            "type": "number",
            "format": "double"
          },
          "y": {
            "type": "number",
            "format": "double"
          }
        },
        "additionalProperties": false
      },
      "ArrangedOutput": {
        "type": "object",
        "description": "One enabled output's placement.",
        "required": [
          "key",
          "slice"
        ],
        "properties": {
          "key": {
            "type": "string",
            "description": "[`super::desired::OutputMatch::key`] of the output this places."
          },
          "slice": {
            "$ref": "#/components/schemas/CanvasRect",
            "description": "The normalized canvas rectangle this output samples."
          }
        }
      },
      "Arrangement": {
        "type": "object",
        "description": "The resolved grid arrangement last applied to a document.\n\nAll five values are resolved: whichever of overlap or content scale the\nclient supplied, this records what the solver settled on. When overlaps\nwere requested they come back exactly as requested and only the content\nscale is derived; when a content scale was requested the overlaps are\nderived from it. It is a record of *intent*, not canonical geometry — the slice\nrectangles remain the truth, and a later manual geometry edit leaves this\nin place. See [`in_effect`] for the test of whether it still describes the\ndocument.",
        "required": [
          "rows",
          "columns",
          "overlapX",
          "overlapY",
          "contentScale"
        ],
        "properties": {
          "columns": {
            "type": "integer",
            "format": "int32",
            "description": "Grid columns; at least 1.",
            "minimum": 0
          },
          "contentScale": {
            "type": "number",
            "format": "double",
            "description": "Output pixels per canvas pixel; `1.0` samples the canvas 1:1."
          },
          "overlapX": {
            "type": "number",
            "format": "double",
            "description": "Fraction of the smaller of two adjacent columns' raster widths that\nthey share; `0 <= overlapX < 1`. Exactly the requested value in\noverlap mode."
          },
          "overlapY": {
            "type": "number",
            "format": "double",
            "description": "As `overlapX`, for adjacent rows' raster heights."
          },
          "rows": {
            "type": "integer",
            "format": "int32",
            "description": "Grid rows; at least 1.",
            "minimum": 0
          }
        },
        "additionalProperties": false
      },
      "ArrangementDryRun": {
        "type": "object",
        "description": "A solved grid arrangement, without writing it: [`crate::model::ArrangementSolution`]'s\nfields, copied rather than `#[serde(flatten)]`'d — [`Arrangement`] denies\nunknown fields, and serde cannot combine that with `flatten` — plus the\ndocument identity it was solved against, so a client can tell whether its\nnext write should still expect to land cleanly — and, for an overlap\nrequest, the overlap range each axis can take from here.",
        "required": [
          "arrangement",
          "unusedCanvas",
          "impliedAspect",
          "outputs",
          "warnings",
          "revision",
          "generation"
        ],
        "properties": {
          "arrangement": {
            "$ref": "#/components/schemas/Arrangement",
            "description": "The resolved five values. In overlap mode `overlapX` and `overlapY`\nare exactly the requested ones: the solver never changes an overlap."
          },
          "generation": {
            "type": "integer",
            "format": "int64",
            "minimum": 0
          },
          "impliedAspect": {
            "type": "number",
            "format": "double"
          },
          "limits": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/ArrangementLimits",
                "description": "Per-axis overlap limits for this grid on this canvas, each holding\nthe other axis at its requested value; see\n[`crate::model::limits`]. Computed against the same document the\nsolve used. Omitted for a `contentScale` request, which has no\noverlaps to limit."
              }
            ]
          },
          "outputs": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ArrangedOutput"
            }
          },
          "overhang": {
            "$ref": "#/components/schemas/Overhang"
          },
          "revision": {
            "type": "integer",
            "format": "int64",
            "minimum": 0
          },
          "unusedCanvas": {
            "$ref": "#/components/schemas/UnusedCanvas"
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "ArrangementLimits": {
        "type": "object",
        "description": "Per-axis overlap limits for one grid on one canvas; see [`limits`].",
        "required": [
          "overlapX",
          "overlapY"
        ],
        "properties": {
          "overlapX": {
            "$ref": "#/components/schemas/AxisLimits"
          },
          "overlapY": {
            "$ref": "#/components/schemas/AxisLimits"
          }
        }
      },
      "ArrangementRequest": {
        "type": "object",
        "description": "What a client asks for: the grid, plus *either* the overlaps *or* the\ncontent scale.\n\nThe overlaps are exact: the solver either honors them as given, choosing\nthe content scale so the grid covers the whole canvas (overhanging it on\nthe axis whose fit is larger), or refuses the request. It never changes an\noverlap. Supplying both overlaps and a content scale is over-determined\nand is an error; supplying neither means both overlaps are zero.",
        "required": [
          "rows",
          "columns"
        ],
        "properties": {
          "allowUnusedCanvas": {
            "type": "boolean",
            "description": "Accept a solution that leaves part of the canvas uncovered.\n\nDefault `false`: overlap requests cover the whole canvas, overhanging\nit on the axis whose fit is larger (`contentScale = min(fitX, fitY)`),\nso they never leave a band; a content-scale solve that would leave\none (an unseamed axis falling short of the canvas at the asked-for\nscale) is refused with a `422` naming the axis, the band as a\npercentage of the canvas, and the aspect that would close it. `true`\ninstead fits the whole grid *inside* the canvas at the requested\noverlaps — `contentScale = max(fitX, fitY)`, since a smaller scale\nwould push a slice past the canvas edge and a larger one would cover\nless on both axes — and reports the band that leaves.\n\n**Behavior change from Suede 0.1.14**: a request that used to succeed\nwith a band left on the canvas (any single-row or single-column grid\non a mismatched canvas aspect, for instance) now overhangs the canvas\ninstead; set this to `true` to get the old fit-inside answer and its\nband back."
          },
          "columns": {
            "type": "integer",
            "format": "int32",
            "minimum": 0
          },
          "committed": {
            "type": "boolean",
            "description": "As on `PUT /config`: `false` applies to the outputs and leaves disk\nuntouched, `true` persists. The solver itself ignores this."
          },
          "contentScale": {
            "type": [
              "number",
              "null"
            ],
            "format": "double"
          },
          "overlapX": {
            "type": [
              "number",
              "null"
            ],
            "format": "double"
          },
          "overlapY": {
            "type": [
              "number",
              "null"
            ],
            "format": "double"
          },
          "rows": {
            "type": "integer",
            "format": "int32",
            "minimum": 0
          }
        }
      },
      "ArrangementSolution": {
        "type": "object",
        "description": "A solved arrangement: what would be written, without writing it.",
        "required": [
          "arrangement",
          "unusedCanvas",
          "impliedAspect",
          "outputs",
          "warnings"
        ],
        "properties": {
          "arrangement": {
            "$ref": "#/components/schemas/Arrangement",
            "description": "The resolved five values, exactly as [`apply`] records them."
          },
          "impliedAspect": {
            "type": "number",
            "format": "double",
            "description": "The canvas aspect at which the requested overlaps would fill both\naxes exactly, with no overhang and no band. Reported, never applied:\nchanging the aspect resizes the headless canvas and the browser, so\nit is the operator's decision."
          },
          "outputs": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ArrangedOutput"
            },
            "description": "Every enabled output, in document order."
          },
          "overhang": {
            "$ref": "#/components/schemas/Overhang",
            "description": "How far the grid overshoots the canvas on each axis, as a fraction of\nthe canvas extent; see [`Overhang`]."
          },
          "unusedCanvas": {
            "$ref": "#/components/schemas/UnusedCanvas",
            "description": "The uncovered fraction of the canvas on each axis; see\n[`UnusedCanvas`] for when this is non-zero."
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Advisory notes about the solution; never a reason to refuse it."
          }
        }
      },
      "ArrangementStatus": {
        "type": "object",
        "description": "The persisted grid-arrangement record from the effective document, and\nwhether re-solving it still reproduces the current slices. See\n[`crate::model::in_effect`]: the record is intent, not canonical geometry,\nso a later manual geometry edit leaves it in place and turns `inEffect`\nfalse rather than reverting or dropping it.",
        "required": [
          "inEffect"
        ],
        "properties": {
          "arrangement": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/Arrangement"
              }
            ]
          },
          "inEffect": {
            "type": "boolean"
          },
          "limits": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/ArrangementLimits",
                "description": "Per-axis overlap limits for the recorded grid on the current canvas\nand outputs, each holding the other axis at its recorded overlap —\nwhat the dry run would report for an overlap request at the recorded\nvalues, with `allowUnusedCanvas` at its default `false` (the record\ndoes not keep it). Lets a client seed its overlap controls and their\nranges in one read. Omitted when there is no record, or when the\nlimits cannot be computed against the current document (no canvas,\na missing raster, too many enabled outputs for the recorded grid, and\nso on); never a reason to fail this read."
              }
            ]
          }
        }
      },
      "AudioConfig": {
        "type": "object",
        "description": "Where an application's audio should go.\n\nOmitting this field entirely is the third option and a different one:\nit defers the choice to each launch, so the app follows whatever\nPipeWire's default sink is at the time. Naming a sink here locks the app\nto it however the machine's default moves.",
        "properties": {
          "gainDb": {
            "type": "number",
            "format": "double",
            "description": "Playback gain for that sink, in dB. `0.0` is unity, and the default.\n\nUnity is the default because it is the only level that means the same\nthing on every machine. A sink left wherever some earlier session put\nit passes signal at a level nobody knows, which is a miserable fault to\nchase: everything works, quietly. On a digital output it is worse than\ninconvenient — every dB taken here is resolution discarded before the\nlink, and nothing downstream can put it back. Attenuate at the\namplifier instead, and leave this alone.\n\nThe scale runs from `-100.0`, which means silence rather than a very\nsmall gain, up to `0.0`. There is nothing above unity: a digital sink\nhas no headroom above full scale and can only clip.\n\nMind the scales when comparing with other tools. PipeWire's own\n`channelVolumes` is linear amplitude; the number `wpctl` prints is its\ncube root, so `wpctl`'s 0.40 is -24 dB, not -8."
          },
          "output": {
            "type": [
              "string",
              "null"
            ],
            "description": "PipeWire `node.name` of the sink to lock this app to. `null` locks it\nto silence."
          }
        },
        "additionalProperties": false
      },
      "AudioSink": {
        "type": "object",
        "description": "An audio sink reported by PipeWire.",
        "required": [
          "id",
          "isNullSink",
          "isDefault"
        ],
        "properties": {
          "description": {
            "type": [
              "string",
              "null"
            ],
            "description": "Human-readable `node.description`."
          },
          "gainDb": {
            "type": [
              "number",
              "null"
            ],
            "format": "double",
            "description": "Current playback gain in dB, `0.0` being unity. `None` when PipeWire\nreports no volume for this sink at all.\n\nDerived from the linear `channelVolumes` PipeWire holds, so it is the\ngain actually in the signal path rather than the cube-rooted number a\nmixer UI displays."
          },
          "id": {
            "type": "string",
            "description": "PipeWire `node.name` — stable across reboots and replugging."
          },
          "isDefault": {
            "type": "boolean",
            "description": "True when this is PipeWire's current default sink."
          },
          "isNullSink": {
            "type": "boolean",
            "description": "True for sinks that discard whatever is routed to them: the one\nSuede manages for silent routing, and the dummy PipeWire falls back\nto when it can find no audio devices at all. Never a real output."
          },
          "outputHint": {
            "type": [
              "string",
              "null"
            ],
            "description": "Video connector this sink is associated with, where derivable."
          }
        }
      },
      "AudioSource": {
        "type": "object",
        "description": "An audio input (capture) device reported by PipeWire.",
        "required": [
          "id"
        ],
        "properties": {
          "card": {
            "type": [
              "string",
              "null"
            ],
            "description": "ALSA card name (`api.alsa.card.name`), where PipeWire reports one."
          },
          "description": {
            "type": [
              "string",
              "null"
            ],
            "description": "Human-readable `node.description`. This is what a Chromium kiosk's\n`enumerateDevices` labels the device as, since audio devices are\nopened through pipewire-pulse."
          },
          "id": {
            "type": "string",
            "description": "PipeWire `node.name` — stable across reboots and replugging."
          }
        }
      },
      "AvDevices": {
        "type": "object",
        "description": "Every audio and video device PipeWire currently reports: outputs Suede\ncan route to, and inputs an app might ask to be given.",
        "required": [
          "audioOutputs",
          "audioInputs",
          "videoInputs"
        ],
        "properties": {
          "audioInputs": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AudioSource"
            },
            "description": "Audio sources (`Audio/Source` nodes)."
          },
          "audioOutputs": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AudioSink"
            },
            "description": "Audio sinks. The only list with a notion of default."
          },
          "videoInputs": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/VideoSource"
            },
            "description": "Video sources (`Video/Source` nodes), from WirePlumber's v4l2 monitor."
          }
        }
      },
      "AxisLimits": {
        "type": "object",
        "description": "The overlaps one axis can take, holding the other axis at its requested\nvalue; see [`limits`].",
        "required": [
          "min",
          "max",
          "hasSeam"
        ],
        "properties": {
          "hasSeam": {
            "type": "boolean",
            "description": "Whether the axis has a seam at all. `false` for a single row\n(`overlapY`) or a single column (`overlapX`): the overlap is then\ninert — it changes nothing — and a client should disable its control.\nAn inert axis reports the whole `[0, 1)` range."
          },
          "max": {
            "type": "number",
            "format": "double",
            "description": "The largest valid overlap found. Always below `1`, since an overlap\nof `1` is never valid: the bound is exclusive, so this reports the\nlargest value that was actually checked and found valid."
          },
          "min": {
            "type": "number",
            "format": "double",
            "description": "The smallest valid overlap; `0` unless an edge slice would otherwise\nfall off the canvas."
          }
        }
      },
      "Background": {
        "type": "object",
        "description": "What an output shows behind, or instead of, any window.\n\nAn appliance with a blank display looks broken even when it is merely\nbetween launches, so a background gives it something deliberate to show\nwhile a browser restarts or before the first app starts.",
        "properties": {
          "color": {
            "type": [
              "string",
              "null"
            ],
            "description": "`#rrggbb`, shown where the wallpaper does not reach, or on its own.\nAbsent means [`DEFAULT_BACKGROUND_COLOR`]."
          },
          "mode": {
            "$ref": "#/components/schemas/BackgroundMode"
          },
          "wallpaper": {
            "type": [
              "string",
              "null"
            ],
            "description": "Id of an uploaded wallpaper. Absent means use `color` alone."
          }
        }
      },
      "BackgroundMode": {
        "type": "string",
        "description": "How a wallpaper is scaled onto an output, matching `sway-output(5)`.",
        "enum": [
          "fill",
          "fit",
          "stretch",
          "center",
          "tile"
        ]
      },
      "BackgroundPreset": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Background"
          },
          {
            "type": "object",
            "required": [
              "id"
            ],
            "properties": {
              "id": {
                "type": "string",
                "description": "Client-chosen name, referenced from an output's `background`."
              }
            }
          }
        ],
        "description": "A named background, defined once and used by any number of outputs."
      },
      "BackgroundRef": {
        "oneOf": [
          {
            "type": "string",
            "description": "Id of an entry in [`DesiredState::backgrounds`]."
          },
          {
            "$ref": "#/components/schemas/Background",
            "description": "Properties given directly."
          }
        ],
        "description": "What an output's `background` may be.\n\nA bare string names a preset; an object spells the properties out. Both are\naccepted because they serve different callers: the UI wants one dropdown\nacross every output, while a script driving the API directly should not\nhave to create a preset to paint one output.\n\n```json\n\"background\": \"lobby\"\n\"background\": { \"wallpaper\": \"teal\", \"mode\": \"fill\", \"color\": \"#101820\" }\n```"
      },
      "BlackLift": {
        "oneOf": [
          {
            "type": "number",
            "format": "double",
            "description": "A bare number has always meant fixed lift."
          },
          {
            "$ref": "#/components/schemas/BlackLiftMode",
            "description": "A tagged fixed or adaptive configuration."
          }
        ],
        "description": "Black lift's backwards-compatible number or its explicitly tagged form."
      },
      "BlackLiftMode": {
        "oneOf": [
          {
            "type": "object",
            "description": "Fixed lift using the same arithmetic as the legacy numeric form.",
            "required": [
              "level",
              "mode"
            ],
            "properties": {
              "level": {
                "type": "number",
                "format": "double"
              },
              "mode": {
                "type": "string",
                "enum": [
                  "fixed"
                ]
              }
            }
          },
          {
            "type": "object",
            "description": "Lift controlled by source-canvas luminance.",
            "required": [
              "level",
              "mode"
            ],
            "properties": {
              "brightThreshold": {
                "type": "number",
                "format": "double"
              },
              "darkThreshold": {
                "type": "number",
                "format": "double"
              },
              "fallMs": {
                "type": "number",
                "format": "double"
              },
              "level": {
                "type": "number",
                "format": "double"
              },
              "mode": {
                "type": "string",
                "enum": [
                  "adaptive"
                ]
              },
              "riseMs": {
                "type": "number",
                "format": "double"
              },
              "slewPerSecond": {
                "type": "number",
                "format": "double"
              }
            }
          }
        ],
        "description": "Explicit black-lift modes. The tag is the JSON `mode` field."
      },
      "CanvasConfig": {
        "type": "object",
        "description": "Canvas aspect and the operator-selected render width.",
        "required": [
          "aspect",
          "renderWidth"
        ],
        "properties": {
          "aspect": {
            "type": "number",
            "format": "double"
          },
          "renderWidth": {
            "type": "integer",
            "format": "int32",
            "minimum": 0
          }
        },
        "additionalProperties": false
      },
      "CanvasRect": {
        "type": "object",
        "description": "A canvas rectangle in isotropic canvas units.",
        "required": [
          "x",
          "y",
          "width",
          "height"
        ],
        "properties": {
          "height": {
            "type": "number",
            "format": "double"
          },
          "width": {
            "type": "number",
            "format": "double"
          },
          "x": {
            "type": "number",
            "format": "double"
          },
          "y": {
            "type": "number",
            "format": "double"
          }
        },
        "additionalProperties": false
      },
      "CapabilityOutcome": {
        "type": "object",
        "description": "How a capability check ended.",
        "required": [
          "completed",
          "elapsedMs"
        ],
        "properties": {
          "completed": {
            "type": "boolean",
            "description": "Whether the page reported before the browser was taken down."
          },
          "elapsedMs": {
            "type": "integer",
            "format": "int64",
            "minimum": 0
          },
          "note": {
            "type": [
              "string",
              "null"
            ],
            "description": "When not completed: what happened instead, including the browser's\nlast words to stderr where it left any."
          },
          "report": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/CapabilityReport"
              }
            ]
          }
        }
      },
      "CapabilityReport": {
        "type": "object",
        "description": "What a browser measured about itself, posted back by the capability page.\n\nObserved state like any other, except the observer is the browser: these\nare the media APIs' own answers from inside the operator's exact\nconfiguration, not an inspection from outside it. The page constructs\nexactly this shape; anything else is drift between the two halves and is\nrejected loudly.",
        "required": [
          "userAgent",
          "webgpu",
          "videoDecoderApi",
          "notes",
          "codecs"
        ],
        "properties": {
          "codecs": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CodecSupport"
            }
          },
          "gpuRenderer": {
            "type": [
              "string",
              "null"
            ]
          },
          "gpuVendor": {
            "type": [
              "string",
              "null"
            ],
            "description": "From `WEBGL_debug_renderer_info`. `null` when WebGL is unavailable or\nthe browser masks it — itself a finding, since a software rasterizer\nusually announces itself here (`SwiftShader`, `llvmpipe`)."
          },
          "notes": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Anything the page could not measure, in its own words."
          },
          "userAgent": {
            "type": "string"
          },
          "videoDecoderApi": {
            "type": "boolean",
            "description": "Whether the WebCodecs `VideoDecoder` API exists at all — without it\nthe per-codec `hardware` column cannot be measured."
          },
          "webgpu": {
            "type": "boolean",
            "description": "Whether a WebGPU adapter was obtainable."
          }
        },
        "additionalProperties": false
      },
      "CaptureIntervals": {
        "type": "object",
        "description": "How many canvas periods elapsed between one capture reaching the slicer\nand the previous one, bucketed over the reporting interval. A period is\n`1000 / canvas refresh` when the canvas output reports one, else the\n16.667 ms of an assumed 60 Hz. A healthy capture loop that keeps up with\nevery canvas frame counts almost entirely in `one`; a loop that can only\nmanage every second frame (the four-projector rig's CPU path before the\nGPU one existed — see `crate::projection::gpu`'s module doc) counts\nalmost entirely in `two`.",
        "required": [
          "one",
          "two",
          "three",
          "more"
        ],
        "properties": {
          "more": {
            "type": "integer",
            "format": "int32",
            "description": "More than 3.5 periods since the previous capture.",
            "minimum": 0
          },
          "one": {
            "type": "integer",
            "format": "int32",
            "minimum": 0
          },
          "three": {
            "type": "integer",
            "format": "int32",
            "minimum": 0
          },
          "two": {
            "type": "integer",
            "format": "int32",
            "minimum": 0
          }
        }
      },
      "Check": {
        "type": "object",
        "description": "An environment health check, as served by `GET /system/checks`.",
        "required": [
          "id",
          "title",
          "status",
          "detail",
          "fixAvailable"
        ],
        "properties": {
          "detail": {
            "type": "string",
            "description": "Explanation of the current result."
          },
          "docsUrl": {
            "type": [
              "string",
              "null"
            ],
            "description": "Link to the documentation page describing manual resolution."
          },
          "fixAvailable": {
            "type": "boolean",
            "description": "Whether `POST /system/checks/{id}/fix` can remediate this check."
          },
          "fixDescription": {
            "type": [
              "string",
              "null"
            ],
            "description": "What the fix would do, shown before it is invoked."
          },
          "id": {
            "type": "string",
            "description": "Stable identifier, e.g. `sway-socket`."
          },
          "status": {
            "$ref": "#/components/schemas/CheckStatus"
          },
          "title": {
            "type": "string",
            "description": "Short human-readable name."
          }
        }
      },
      "CheckStatus": {
        "type": "string",
        "description": "Result of a single environment health check.",
        "enum": [
          "pass",
          "warn",
          "fail"
        ]
      },
      "CheckSummary": {
        "type": "object",
        "description": "How the environment health checks last came out, tallied by status.\n\nThe field names are exactly [`CheckStatus`]'s variants: no second\nvocabulary for the same three words.",
        "required": [
          "pass",
          "warn",
          "fail"
        ],
        "properties": {
          "fail": {
            "type": "integer",
            "format": "int32",
            "minimum": 0
          },
          "pass": {
            "type": "integer",
            "format": "int32",
            "minimum": 0
          },
          "warn": {
            "type": "integer",
            "format": "int32",
            "minimum": 0
          }
        }
      },
      "CodecSupport": {
        "type": "object",
        "description": "One codec at one resolution, as the media APIs answered.",
        "required": [
          "label",
          "contentType",
          "supported"
        ],
        "properties": {
          "contentType": {
            "type": "string",
            "description": "What was actually asked for, e.g. `video/mp4; codecs=\"hvc1.1.6.L153.B0\"`."
          },
          "hardware": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "`VideoDecoder.isConfigSupported` with `prefer-hardware`: `true` means\na hardware decoder accepted the configuration, `false` means only a\nsoftware one did, `null` means the API could not answer."
          },
          "label": {
            "type": "string",
            "description": "Human label, e.g. `H.265 Main 2160p60`."
          },
          "powerEfficient": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "The classic hardware-decode signal; browsers report it conservatively."
          },
          "smooth": {
            "type": [
              "boolean",
              "null"
            ]
          },
          "supported": {
            "type": "boolean",
            "description": "`MediaCapabilities.decodingInfo().supported`."
          }
        },
        "additionalProperties": false
      },
      "ConfigChange": {
        "type": "object",
        "description": "Payload of the `config_changed` event, published on every change to the\none shared effective document: a preview PUT, a commit, a revert,\nreconciler adoption, or a load-time repair. There is no per-client\nworking copy and nothing stops one client from committing or reverting\nanother's unsaved edit — every connected client, including third-party\napps, learns of the change through this event rather than being blocked\nfrom making it.",
        "required": [
          "revision",
          "generation",
          "epoch",
          "committed",
          "section",
          "config"
        ],
        "properties": {
          "committed": {
            "type": "boolean",
            "description": "Mirrors `config.committed`: `false` while this change leaves a\nworking copy live, `true` for a commit, a revert, an adoption, or a\nrepair (which only ever touch the saved document)."
          },
          "config": {
            "$ref": "#/components/schemas/DesiredState",
            "description": "The effective document that this change produced: the working copy\nif one exists, else the saved one. A client applies this directly\nrather than re-fetching `GET /config`."
          },
          "epoch": {
            "type": "string",
            "description": "Store instance identity, as `X-Config-Epoch`."
          },
          "generation": {
            "type": "integer",
            "format": "int64",
            "description": "In-memory working-copy generation, as `X-Config-Generation`.",
            "minimum": 0
          },
          "revision": {
            "type": "integer",
            "format": "int64",
            "description": "Persisted document revision, as `ETag`/`If-Match`.",
            "minimum": 0
          },
          "section": {
            "type": "string",
            "description": "Which part of the document changed: `all`, `outputs`, `apps`,\n`settings`, `projection`, `backgrounds`, or `repair` for a load-time\nrepair."
          }
        }
      },
      "DesiredState": {
        "type": "object",
        "description": "The complete desired-state document.",
        "properties": {
          "activeApp": {
            "type": [
              "string",
              "null"
            ],
            "description": "Which app is running. `null` runs nothing.\n\nExactly one app is ever active, and it always spans every display —\nthe appliance is a single canvas, not a window manager. Keeping the\nchoice as one pointer makes switching atomic: activating B cannot\nleave A half-enabled the way per-app flags could.",
            "default": null
          },
          "apps": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AppConfig"
            },
            "default": []
          },
          "backgrounds": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BackgroundPreset"
            },
            "description": "Named background definitions outputs can refer to.\n\nA multi-display installation usually wants one look across every\nalternative — repeating a wallpaper, scaling mode and color on each\noutput — makes the common case the laborious one and guarantees the\ndisplays drift apart the first time somebody edits only three of four.",
            "default": []
          },
          "committed": {
            "type": "boolean",
            "description": "Whether this document is persisted, or a working copy being tried out.\n\nOn reads, Suede reports the truth: `true` for the saved document,\n`false` when a working copy is live. On writes, the *client* speaks:\n`committed: true` persists; anything else applies the document to the\noutputs — reconciled immediately, exactly as if saved — but leaves\ndisk untouched, so a restart or `POST /config/revert` returns to the\nsaved state. A UI can therefore push every edit as it happens and\nonly set the flag when the operator presses Save.",
            "default": false
          },
          "outputs": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OutputConfig"
            },
            "default": []
          },
          "projection": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/ProjectionConfig",
                "description": "Multi-projector features. Absent means no projection processing at all."
              }
            ],
            "default": null
          },
          "revision": {
            "type": "integer",
            "format": "int64",
            "description": "Monotonic revision, incremented by Suede on every accepted write.",
            "default": 0,
            "minimum": 0
          },
          "schemaVersion": {
            "type": "integer",
            "format": "int32",
            "description": "Document schema version, managed by Suede.",
            "default": 0,
            "minimum": 0
          },
          "settings": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Settings"
              }
            ],
            "default": {
              "hideCursor": true,
              "measureCapabilitiesOnStart": true,
              "outputPollIntervalSeconds": 5
            }
          }
        },
        "additionalProperties": false
      },
      "DisplayIdentity": {
        "type": "object",
        "description": "Enough of a display's EDID to tell \"the same display\" from \"a different\none on the same connector\".",
        "properties": {
          "make": {
            "type": [
              "string",
              "null"
            ]
          },
          "model": {
            "type": [
              "string",
              "null"
            ]
          },
          "serial": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "additionalProperties": false
      },
      "Divergence": {
        "type": "object",
        "description": "Something desired that could not be realized.",
        "required": [
          "kind",
          "subject",
          "detail"
        ],
        "properties": {
          "detail": {
            "type": "string",
            "description": "Human-readable explanation."
          },
          "docsUrl": {
            "type": [
              "string",
              "null"
            ],
            "description": "Where to read about this kind of problem.\n\nCarried here, rather than left for each client to work out, so that\nanything consuming the API can offer the same help the bundled UI does."
          },
          "kind": {
            "type": "string",
            "description": "Machine-readable kind, e.g. `output_not_connected`."
          },
          "subject": {
            "type": "string",
            "description": "Resource the divergence concerns, e.g. an output name or app id."
          }
        }
      },
      "FixOutcome": {
        "type": "object",
        "description": "What a remediation did.",
        "required": [
          "id",
          "detail"
        ],
        "properties": {
          "detail": {
            "type": "string"
          },
          "id": {
            "type": "string"
          }
        }
      },
      "FrameCost": {
        "type": "object",
        "description": "Where a captured frame's time went in the slicer, in ms, averaged over\nthe reporting interval. See `crate::projection::slicer::FrameStats` for\nwhat each phase covers.",
        "required": [
          "waiting",
          "snapshot",
          "requesting",
          "blending",
          "gpu"
        ],
        "properties": {
          "blending": {
            "type": "number",
            "format": "double"
          },
          "gpu": {
            "type": "number",
            "format": "double",
            "description": "GPU fence wait per frame — see `crate::projection::gpu::Gpu::blend`.\nZero on the CPU path, which never waits on a fence."
          },
          "requesting": {
            "type": "number",
            "format": "double"
          },
          "snapshot": {
            "type": "number",
            "format": "double"
          },
          "waiting": {
            "type": "number",
            "format": "double"
          }
        }
      },
      "GlYieldMode": {
        "type": "string",
        "description": "NVIDIA's `__GL_YIELD` toggle for Sway's environment — experimental; see\n[`crate::config::BootstrapConfig::gl_yield`].\n\nRead once at startup from `suede.toml`'s top-level `gl_yield` key, exactly\nlike [`PresentationMode`] — never overridable by a `SUEDE_*` variable,\nbecause it too describes how the compositor was started. NVIDIA-only:\nMesa ignores `__GL_YIELD` entirely, so this has no effect on other\ndrivers.",
        "enum": [
          "default",
          "usleep",
          "nothing"
        ]
      },
      "GlYieldStatus": {
        "type": "object",
        "description": "What `GET /system` reports about `gl_yield` — requested and live.\n\nExperimental. Unlike [`PresentationStatus`], `effective` is not resolved\nonce at startup and cached: it is read from the live Sway's\n`/proc/<pid>/environ` on every request, so a session that has not been\nrestarted since `suede.toml` changed shows the mismatch instead of a\nstale cached answer.",
        "required": [
          "requested",
          "effective"
        ],
        "properties": {
          "effective": {
            "$ref": "#/components/schemas/GlYieldMode",
            "description": "What the live compositor's environment actually shows right now."
          },
          "requested": {
            "$ref": "#/components/schemas/GlYieldMode",
            "description": "What `suede.toml` asks for."
          }
        }
      },
      "GspFirmware": {
        "type": "string",
        "description": "Whether the GPU Systems Processor firmware is active for this GPU, read\nfrom the `GPU Firmware:` line of\n`/proc/driver/nvidia/gpus/*/information` — see [`crate::nvidia_driver`].",
        "enum": [
          "on",
          "off",
          "unknown"
        ]
      },
      "HeartbeatConfig": {
        "type": "object",
        "description": "Content-level watchdog settings.",
        "required": [
          "enabled"
        ],
        "properties": {
          "enabled": {
            "type": "boolean"
          },
          "startupGraceSeconds": {
            "type": "integer",
            "format": "int64",
            "description": "Time allowed after launch for the first heartbeat to arrive.",
            "minimum": 0
          },
          "timeoutSeconds": {
            "type": "integer",
            "format": "int64",
            "description": "Silence tolerated once armed, before the app is killed and relaunched.",
            "minimum": 0
          }
        },
        "additionalProperties": false
      },
      "LagFrames": {
        "type": "object",
        "description": "Histogram of a per-snapshot lag, in whole refresh periods, behind the\nearliest output to present that snapshot. Only snapshots that at least\ntwo outputs presented contribute a sample; a lone presenting output has\nnothing to lag behind.",
        "required": [
          "zero",
          "one",
          "two",
          "more"
        ],
        "properties": {
          "more": {
            "type": "integer",
            "format": "int32",
            "description": "Three or more whole refresh periods behind.",
            "minimum": 0
          },
          "one": {
            "type": "integer",
            "format": "int32",
            "minimum": 0
          },
          "two": {
            "type": "integer",
            "format": "int32",
            "minimum": 0
          },
          "zero": {
            "type": "integer",
            "format": "int32",
            "minimum": 0
          }
        }
      },
      "LastMeasurement": {
        "type": "object",
        "description": "The stored measurement, as `GET /apps/capabilities/last` serves it.",
        "required": [
          "measuredAt",
          "appId",
          "current"
        ],
        "properties": {
          "appId": {
            "type": "string",
            "description": "Which application's configuration was measured."
          },
          "current": {
            "type": "boolean",
            "description": "Whether the measurement still describes this machine: same launcher,\nsame browser binary, same driver, same daemon. `false` means the\nstartup check will re-measure on the next boot."
          },
          "measuredAt": {
            "type": "integer",
            "format": "int64",
            "description": "Unix seconds.",
            "minimum": 0
          },
          "note": {
            "type": [
              "string",
              "null"
            ]
          },
          "report": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/CapabilityReport"
              }
            ]
          }
        }
      },
      "LaunchPreview": {
        "type": "object",
        "description": "Exactly what an application would be launched as.",
        "required": [
          "searched",
          "args",
          "env",
          "wipeProfile"
        ],
        "properties": {
          "args": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PreviewItem"
            }
          },
          "captureGrantOrigin": {
            "type": [
              "string",
              "null"
            ],
            "description": "The origin that will be granted persistent camera/microphone access\nin the profile above, before launch. Present only when a grant will\nactually be written — `grantCapture` is on and the URI has an\n`http`/`https` origin — not merely when the launcher is chromium."
          },
          "env": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PreviewItem"
            }
          },
          "profileDir": {
            "type": [
              "string",
              "null"
            ],
            "description": "Browser profile directory, when the launcher manages one."
          },
          "program": {
            "type": [
              "string",
              "null"
            ],
            "description": "The binary that would run, resolved on this machine. `null` when none\nof the candidates is installed."
          },
          "searched": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "What was looked for, in order. A single entry when the application\nnames its own program."
          },
          "wipeProfile": {
            "type": "boolean",
            "description": "Whether that directory is emptied before each launch."
          }
        }
      },
      "Launcher": {
        "oneOf": [
          {
            "type": "object",
            "description": "Chromium with Suede's kiosk argument set.",
            "required": [
              "uri",
              "kind"
            ],
            "properties": {
              "extraArgs": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "Appended after the preset's arguments."
              },
              "grantCapture": {
                "type": "boolean",
                "description": "Grant the page's own origin camera and microphone access in its\nprivate profile, so it can enumerate capture devices and choose\none by name rather than only ever getting whatever device the\nbrowser hands it first. On by default.\n\nAs with the autoplay and capture-prompt bypass above, the operator\nchoosing what this machine runs is the consent a permission\nprompt would otherwise collect."
              },
              "kind": {
                "type": "string",
                "enum": [
                  "chromium-kiosk"
                ]
              },
              "program": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Which binary to launch, overriding the search.\n\nSuede normally tries `chromium`, `chromium-browser`,\n`google-chrome-stable` and `google-chrome` in that order,\npreferring any that is not a snap. Name one here to settle it —\na bare name is looked up on `PATH`, a path is used as given."
              },
              "showFpsCounter": {
                "type": "boolean"
              },
              "uri": {
                "type": "string",
                "description": "URI to load. Supports `{appId}` and `{heartbeatUrl}` placeholders."
              }
            }
          },
          {
            "type": "object",
            "description": "Firefox with its kiosk argument set.",
            "required": [
              "uri",
              "kind"
            ],
            "properties": {
              "extraArgs": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "kind": {
                "type": "string",
                "enum": [
                  "firefox-kiosk"
                ]
              },
              "program": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Which binary to launch, overriding the search. See\n[`Launcher::ChromiumKiosk::program`]."
              },
              "uri": {
                "type": "string"
              }
            }
          },
          {
            "type": "object",
            "description": "Any executable, launched verbatim.",
            "required": [
              "command",
              "kind"
            ],
            "properties": {
              "args": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "command": {
                "type": "string"
              },
              "kind": {
                "type": "string",
                "enum": [
                  "exec"
                ]
              }
            }
          }
        ],
        "description": "How an application is launched."
      },
      "Mode": {
        "type": "object",
        "description": "A display mode. Refresh is in Hz (Sway reports mHz on the wire).",
        "required": [
          "width",
          "height",
          "refreshHz"
        ],
        "properties": {
          "height": {
            "type": "integer",
            "format": "int32"
          },
          "refreshHz": {
            "type": "number",
            "format": "double"
          },
          "width": {
            "type": "integer",
            "format": "int32"
          }
        }
      },
      "NvidiaDriverStatus": {
        "type": "object",
        "description": "What `GET /system` reports about the NVIDIA driver, or `null` on a\nmachine `/proc/driver/nvidia` does not exist on — see\n[`crate::nvidia_driver::detect`].",
        "required": [
          "version",
          "kernelModule",
          "gspFirmware",
          "gspOptional",
          "newestTested"
        ],
        "properties": {
          "gspFirmware": {
            "$ref": "#/components/schemas/GspFirmware"
          },
          "gspOptional": {
            "type": "boolean",
            "description": "True only for the proprietary module: the open module requires GSP\nand has no way to disable it."
          },
          "kernelModule": {
            "$ref": "#/components/schemas/NvidiaKernelModule"
          },
          "newestTested": {
            "type": "string",
            "description": "The newest driver release Suede's direct and Wayland presentation\npaths have been validated on — see\n[`crate::nvidia_driver::NEWEST_TESTED_NVIDIA_DRIVER`]. Reported\nalongside `version` so a client can compare them without also\nshipping the constant, and so the `nvidia-driver-version` health\ncheck's warning is self-explanatory from the API alone."
          },
          "version": {
            "type": "string",
            "description": "The dotted version NVRM reports, e.g. `595.91.07`."
          }
        }
      },
      "NvidiaKernelModule": {
        "type": "string",
        "description": "Which NVIDIA kernel module is loaded, read from the `NVRM version:` line\nof `/proc/driver/nvidia/version` — see [`crate::nvidia_driver`].",
        "enum": [
          "proprietary",
          "open"
        ]
      },
      "Output": {
        "type": "object",
        "description": "A video output as reported by Sway's `get_outputs`.",
        "required": [
          "name",
          "active",
          "modes",
          "rect"
        ],
        "properties": {
          "active": {
            "type": "boolean",
            "description": "Whether Sway currently has this output enabled."
          },
          "adaptiveSyncStatus": {
            "type": [
              "string",
              "null"
            ]
          },
          "currentMode": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/Mode",
                "description": "Mode currently applied."
              }
            ]
          },
          "make": {
            "type": [
              "string",
              "null"
            ],
            "description": "EDID manufacturer."
          },
          "model": {
            "type": [
              "string",
              "null"
            ],
            "description": "EDID model."
          },
          "modes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Mode"
            },
            "description": "All modes the display advertises, deduplicated."
          },
          "name": {
            "type": "string",
            "description": "Connector name, e.g. `HDMI-A-1`."
          },
          "rect": {
            "$ref": "#/components/schemas/Rect",
            "description": "Position and size in the global layout."
          },
          "scale": {
            "type": [
              "number",
              "null"
            ],
            "format": "double"
          },
          "serial": {
            "type": [
              "string",
              "null"
            ],
            "description": "EDID serial, where the display provides one."
          },
          "transform": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "OutputAlignmentResult": {
        "type": "string",
        "description": "The outcome behind [`OutputAlignmentStatus::last_result`].",
        "enum": [
          "inPhase",
          "outOfPhase",
          "aligned",
          "stillOutOfPhase",
          "gaveUp",
          "failed",
          "notJudged"
        ]
      },
      "OutputAlignmentStatus": {
        "type": "object",
        "description": "What `GET /system` reports about automatic output phase alignment: the\ndaemon re-aligning the displays itself, with the `output-phase` check's\nown fix, after a Wayland session starts them out of phase — see\n[`crate::alignment`].",
        "required": [
          "enabled",
          "attempts"
        ],
        "properties": {
          "attempts": {
            "type": "integer",
            "format": "int32",
            "description": "Alignments run this compositor session, at most three. Resets when the\ncompositor or the set of active displays changes.",
            "minimum": 0
          },
          "enabled": {
            "type": "boolean",
            "description": "`align_outputs` in `suede.toml` (default `true`)."
          },
          "lastPhaseMs": {
            "type": [
              "number",
              "null"
            ],
            "format": "double",
            "description": "The largest absolute `phaseMs` of the last judged slicer interval, or\n`null` before one has been judged."
          },
          "lastResult": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/OutputAlignmentResult",
                "description": "What the last judgment or attempt concluded, or `null` before\nanything has been judged this session."
              }
            ]
          }
        }
      },
      "OutputConfig": {
        "type": "object",
        "description": "Desired configuration for one output.",
        "required": [
          "match"
        ],
        "properties": {
          "adaptiveSync": {
            "type": "boolean"
          },
          "adopted": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/AdoptedOutput",
                "description": "What Suede observed and pinned because the configuration named none.\n\nKept apart from the operator's own fields, not merged into them, because\nthe two must stay distinguishable forever: an operator who asked for\n1920x1200 and cannot have it deserves a divergence that persists until a\nhuman decides, while a value that is merely what happened to be plugged in\nlast week should be replaced without ceremony when the display changes.\nMerged into one field, nothing could tell those two cases apart."
              }
            ]
          },
          "allowTearing": {
            "type": "boolean",
            "description": "Applied only when the detected Sway version supports it (≥ 1.10)."
          },
          "arrangeOffset": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/ArrangeOffset",
                "description": "A per-output nudge applied only by the grid arrangement solve; see\n[`ArrangeOffset`]. Absent means `{0, 0}` — the common case, and the\nonly value the reference UI ever writes (it locks the field at zero\nbut preserves whatever a third party set here)."
              }
            ]
          },
          "background": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/BackgroundRef",
                "description": "What this output shows when no window covers it: a preset name, or the\nproperties spelled out. See [`BackgroundRef`]."
              }
            ]
          },
          "enable": {
            "type": "boolean",
            "description": "Whether the output should be enabled. `false` actively disables it."
          },
          "geometry": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/OutputGeometry",
                "description": "Shared normalized content slice plus retained Warp correction and\nindependent physical light footprint. Simple uses the same slice.\nKept independently from `position` and from the requested pipeline so\nswitching to simple mode does not discard calibrated warp settings."
              }
            ]
          },
          "match": {
            "$ref": "#/components/schemas/OutputMatch",
            "description": "Which physical output this entry applies to."
          },
          "maxRenderTimeMs": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int32",
            "description": "Maximum milliseconds allowed to render a frame; `null` means off.",
            "minimum": 0
          },
          "mode": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/Mode",
                "description": "Mode to apply. When absent, Sway's preferred mode is left in place —\nand, once settled, pinned into `adopted.mode`. Reading this field\ndirectly sees only the operator's own choice; almost every reader\nwants [`OutputConfig::effective_mode`] instead."
              }
            ]
          },
          "position": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/Position",
                "description": "Position in the global layout. Suede performs no layout arithmetic.\nNever adopted — see [`AdoptedOutput`]."
              }
            ]
          },
          "scale": {
            "type": [
              "number",
              "null"
            ],
            "format": "double",
            "description": "When absent, left as Sway's own and, once settled, pinned into\n`adopted.scale`. Prefer [`OutputConfig::effective_scale`]."
          },
          "transform": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/Transform",
                "description": "When absent, left as Sway's own and, once settled, pinned into\n`adopted.transform`. Prefer [`OutputConfig::effective_transform`]."
              }
            ]
          }
        },
        "additionalProperties": false
      },
      "OutputGeometry": {
        "type": "object",
        "description": "Persisted shared content selection and independent destination correction.",
        "required": [
          "slice",
          "corners",
          "rasterFootprint"
        ],
        "properties": {
          "center": {
            "type": "array",
            "items": {
              "type": "number",
              "format": "double"
            }
          },
          "corners": {
            "type": "array",
            "items": {
              "type": "array",
              "items": {
                "type": "number",
                "format": "double"
              }
            },
            "description": "Output-local normalized destination pins in TL, TR, BR, BL order."
          },
          "rasterFootprint": {
            "$ref": "#/components/schemas/CanvasRect",
            "description": "Physical light-field coverage, independent of the slice and\ndestination pins. Until a calibrated footprint is supplied, conversion\ncopies the slice rectangle into this field."
          },
          "slice": {
            "$ref": "#/components/schemas/CanvasRect",
            "description": "The slice: the canonical normalized canvas region this output\nsamples, used by both Simple and Warp. Pixel crop origins and content\nenlargement are derived from this rectangle, never stored as a second\neditable representation.\n\nSerialized as `slice`; `source` is still accepted on input (an alpha\nrename) so saved documents and older clients' writes keep parsing,\nbut every response and every re-serialization emits `slice`."
          }
        },
        "additionalProperties": false
      },
      "OutputMatch": {
        "type": "object",
        "description": "Rule selecting which physical output a config entry applies to.",
        "properties": {
          "make": {
            "type": [
              "string",
              "null"
            ],
            "description": "EDID manufacturer."
          },
          "model": {
            "type": [
              "string",
              "null"
            ],
            "description": "EDID model."
          },
          "name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Connector name, e.g. `HDMI-A-1`. The default and most direct way to match."
          },
          "serial": {
            "type": [
              "string",
              "null"
            ],
            "description": "EDID serial."
          }
        },
        "additionalProperties": false
      },
      "OutputTiming": {
        "type": "object",
        "description": "One output's presentation tally for the interval.",
        "required": [
          "name",
          "presented",
          "discarded",
          "zeroCopyPresented",
          "lagFrames"
        ],
        "properties": {
          "discarded": {
            "type": "integer",
            "format": "int32",
            "minimum": 0
          },
          "lagFrames": {
            "$ref": "#/components/schemas/LagFrames",
            "description": "Histogram of how many whole refresh periods this output's\npresentation landed after the earliest output to present the same\nsnapshot. Derived purely from `wp_presentation` timestamps — the\nflip — so a display's own processing latency between the flip and\nphotons on screen is invisible here: if a camera shows this output\nvisibly behind while this reads all-zero, the lag is in the display,\nnot the presentation path."
          },
          "name": {
            "type": "string"
          },
          "phaseMs": {
            "type": [
              "number",
              "null"
            ],
            "format": "double",
            "description": "This output's vblank phase relative to the first output, in ms, within\nhalf a refresh period either side. Stable across intervals means the\nheads are locked at a fixed offset; wandering means independent clocks."
          },
          "phaseSpreadMs": {
            "type": [
              "number",
              "null"
            ],
            "format": "double",
            "description": "The spread of that phase within the interval (max − min of the\nper-frame value), ms. Near zero means locked; near a period means drifting."
          },
          "presented": {
            "type": "integer",
            "format": "int32",
            "minimum": 0
          },
          "refreshHz": {
            "type": [
              "number",
              "null"
            ],
            "format": "double",
            "description": "From wp_presentation's refresh field: the output's actual refresh interval, as Hz."
          },
          "zeroCopyPresented": {
            "type": "integer",
            "format": "int32",
            "description": "How many of `presented` carried the presentation feedback's\n`zero_copy` flag: the compositor scanned this output's buffer out\ndirectly to the display controller, with no compositing pass, rather\nthan blitting it into its own framebuffer first.",
            "minimum": 0
          }
        }
      },
      "Overhang": {
        "type": "object",
        "description": "The fraction of the canvas extent by which the grid overshoots the canvas\non each axis, measured before any\n[`super::desired::OutputConfig::arrange_offset`] is applied.\n\nThe total for the axis, split evenly between its two ends: the grid is\ncentered, so half of it hangs off before the canvas's first edge and half\npast its last. Slice pixels out there render black. In overlap mode this\nis non-zero on the axis whose fit is the larger (the other fills the\ncanvas exactly); in content-scale mode, on an unseamed axis whose single\nrow or column is taller or wider than the canvas at the asked-for scale.\nAdvisory: the only overhang [`solve`] refuses is one that pushes a whole\nslice off the canvas.",
        "required": [
          "x",
          "y"
        ],
        "properties": {
          "x": {
            "type": "number",
            "format": "double"
          },
          "y": {
            "type": "number",
            "format": "double"
          }
        }
      },
      "PackageVersion": {
        "type": "object",
        "description": "Version of a package relevant to Suede's operation.",
        "required": [
          "name"
        ],
        "properties": {
          "name": {
            "type": "string"
          },
          "version": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "Port": {
        "type": "object",
        "description": "A connector on the graphics hardware, attached or not.",
        "required": [
          "name",
          "connected"
        ],
        "properties": {
          "connected": {
            "type": "boolean",
            "description": "Whether a display is currently attached."
          },
          "name": {
            "type": "string",
            "description": "Connector name as sway would report it, e.g. `DP-2`."
          }
        }
      },
      "Position": {
        "type": "object",
        "required": [
          "x",
          "y"
        ],
        "properties": {
          "x": {
            "type": "integer",
            "format": "int32"
          },
          "y": {
            "type": "integer",
            "format": "int32"
          }
        }
      },
      "PowerOutcome": {
        "type": "object",
        "description": "What `POST /api/v1/system/power` accepted.",
        "required": [
          "verb",
          "detail"
        ],
        "properties": {
          "detail": {
            "type": "string"
          },
          "verb": {
            "$ref": "#/components/schemas/PowerVerb"
          }
        }
      },
      "PowerRequest": {
        "type": "object",
        "description": "Body of `POST /api/v1/system/power`.",
        "required": [
          "verb",
          "confirm"
        ],
        "properties": {
          "confirm": {
            "type": "string",
            "description": "The machine's hostname, repeated back. Proof that the caller meant\nthis machine: a retry, a stray script or a fuzzer does not know it,\nand the cost of being wrong here is a dark video wall mid-show."
          },
          "verb": {
            "$ref": "#/components/schemas/PowerVerb"
          }
        },
        "additionalProperties": false
      },
      "PowerVerb": {
        "type": "string",
        "description": "A host power operation Suede may be permitted to perform.\n\nLives here, not in `desired`, because `GET /system` reports it (so it\nneeds [`ToSchema`]) and it is read by [`crate::config::BootstrapConfig`],\nwhich cannot depend on desired state without an awkward cycle.",
        "enum": [
          "reboot",
          "poweroff"
        ]
      },
      "PresentationMode": {
        "type": "string",
        "description": "How an appliance presents its outputs: the default Wayland path (sway\ntiles or slices, as [`crate::config::BootstrapConfig::allow_overlaps`]\ndecides), or the experimental direct path where the daemon takes\nownership of the physical outputs itself and presents through the\nslicer's Vulkan swapchain, bypassing the compositor entirely.\n\nRead once at startup from `suede.toml`'s top-level `presentation` key,\nexactly like [`crate::config::BootstrapConfig::allow_overlaps`] — never\noverridable by a `SUEDE_*` variable, because it describes how the\ncompositor was started, which a variable on Suede's own process cannot\nchange.",
        "enum": [
          "wayland",
          "direct"
        ]
      },
      "PresentationOffset": {
        "type": "object",
        "description": "Spread between the earliest and latest output to present the same frame,\nfrom `wp_presentation` feedback, in ms.",
        "required": [
          "mean",
          "max"
        ],
        "properties": {
          "max": {
            "type": "number",
            "format": "double"
          },
          "mean": {
            "type": "number",
            "format": "double"
          }
        }
      },
      "PresentationStatus": {
        "type": "object",
        "description": "What `GET /system` reports about presentation — requested, effective, and\nwhy they differ, if they do.\n\nExperimental. `effective` is what this session runs, decided once when\nthe daemon starts (see [`crate::presentation::resolve`]): `direct` only\nwhen the login started a headless-only compositor for it and no\nfallback has happened this boot. A fallback ends the session and the\nnext one reports `wayland` with the fallback's reason until a reboot.",
        "required": [
          "requested",
          "effective",
          "outputs"
        ],
        "properties": {
          "effective": {
            "$ref": "#/components/schemas/PresentationMode",
            "description": "What this session is actually running."
          },
          "outputs": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Physical outputs the daemon owns directly, bypassing the compositor:\nevery connected display while `effective` is `direct`, and empty\notherwise."
          },
          "reason": {
            "type": [
              "string",
              "null"
            ],
            "description": "Why `effective` differs from `requested`, or `null` when they match."
          },
          "requested": {
            "$ref": "#/components/schemas/PresentationMode",
            "description": "What `suede.toml` asked for."
          }
        }
      },
      "PreviewItem": {
        "type": "object",
        "description": "One argument or variable, and where it came from.\n\nThe distinction is the point of the preview: a preset contributes most of\nwhat is launched, and an operator needs to see which parts are theirs to\nchange and which arrive automatically.",
        "required": [
          "value",
          "source"
        ],
        "properties": {
          "name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Variable name. Absent for arguments."
          },
          "source": {
            "type": "string",
            "description": "`preset`, `app`, or `uri` — the expanded URI the preset appends last."
          },
          "value": {
            "type": "string",
            "description": "For an argument this is the argument; for a variable, its value."
          }
        }
      },
      "Problem": {
        "type": "object",
        "description": "RFC 9457 problem document.",
        "required": [
          "type",
          "title",
          "status",
          "detail"
        ],
        "properties": {
          "detail": {
            "type": "string",
            "description": "Human-readable explanation specific to this occurrence."
          },
          "status": {
            "type": "integer",
            "format": "int32",
            "description": "HTTP status code.",
            "minimum": 0
          },
          "title": {
            "type": "string",
            "description": "Short human-readable summary."
          },
          "type": {
            "type": "string",
            "description": "URI reference identifying the error type."
          }
        }
      },
      "ProjectionBlackLiftStatus": {
        "type": "object",
        "description": "Adaptive lift telemetry. Capture IDs count measurements, while logical\ngenerations also advance for retained-capture repaints. Neither is a\npersisted configuration revision or a promise of simultaneous scanout.",
        "required": [
          "metric",
          "available",
          "paused",
          "stale",
          "sampleCount",
          "target",
          "applied",
          "logicalGeneration"
        ],
        "properties": {
          "applied": {
            "type": "number",
            "format": "double"
          },
          "available": {
            "type": "boolean"
          },
          "captureId": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "minimum": 0
          },
          "logicalGeneration": {
            "type": "integer",
            "format": "int64",
            "minimum": 0
          },
          "luminance": {
            "type": [
              "number",
              "null"
            ],
            "format": "double"
          },
          "measuredAtUnixMs": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "description": "Unix timestamp of the most recent valid source measurement.",
            "minimum": 0
          },
          "measurementMs": {
            "type": [
              "number",
              "null"
            ],
            "format": "double",
            "description": "Submission to collection of the most recent sampling pass. The\nmeasurement is asynchronous, so this is pipeline latency and not time\nthe render thread spent waiting: it is bounded below by the pass and\nits readback and above by how soon the next capture collected it."
          },
          "metric": {
            "type": "string"
          },
          "paused": {
            "type": "boolean"
          },
          "reason": {
            "type": [
              "string",
              "null"
            ]
          },
          "sampleAgeMs": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "description": "Age at response serialization, including while static content settles.",
            "minimum": 0
          },
          "sampleCount": {
            "type": "integer",
            "format": "int32",
            "minimum": 0
          },
          "stale": {
            "type": "boolean",
            "description": "The latest capture had no samples; the last valid target is retained."
          },
          "target": {
            "type": "number",
            "format": "double"
          }
        }
      },
      "ProjectionChildGenerations": {
        "type": "object",
        "description": "Control-protocol generation counters, local to the current child\nsession's own sequence. These reset to zero whenever the slicer process\nrestarts and must never be compared against a desired-state document\ngeneration — see [`ProjectionConfigGenerations`] for those.",
        "properties": {
          "accepted": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "description": "Highest accepted generation reported by this child.",
            "minimum": 0
          },
          "applied": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "description": "Highest generation installed as a complete render revision.",
            "minimum": 0
          },
          "built": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "description": "Highest generation built for any affected output.",
            "minimum": 0
          },
          "presented": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "description": "Highest generation presented by any output. Independent heads need\nnot physically present a generation at the same instant.",
            "minimum": 0
          },
          "requested": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "minimum": 0
          },
          "submitted": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "description": "Highest generation submitted by any output. This does not mean all\noutputs have submitted it.",
            "minimum": 0
          }
        }
      },
      "ProjectionConfig": {
        "type": "object",
        "description": "Shared multi-projector content layout and retained Warp correction.\n\nThere is no overlap setting here, because the slice layout determines\noverlap. Simple and Warp share the configured canvas and normalized slice\nrectangles. Warp adds destination correction to that content selection.\nWithout an explicit canvas, integer output positions define the layout.\n\nSway never sees any of this. It is always handed a plain edge-to-edge\ntiling; the active app renders into a headless canvas the size of the\nlayout's bounding box; and the slicer cuts that canvas into each\nprojector's configured rectangle, duplicating the intersections and\nfading them from both sides when `blend` is on.",
        "properties": {
          "arrangement": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/Arrangement",
                "description": "The grid arrangement last applied, as a record of what was asked for.\n\nExplicit geometry stays canonical: this holds the resolved rows,\ncolumns, overlaps and content scale so a slider client can pick up\nwhere the last one left off, and nothing else reads it. A later\nmanual geometry edit leaves it in place — see\n[`crate::model::arrangement::in_effect`], which reports whether it\nstill describes the document's slices."
              }
            ],
            "default": null
          },
          "blackLift": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/BlackLift",
                "description": "Black-level compensation, `0.0` (off) to `0.5`.\n\nProjector black is not zero light, so seams glow in dark scenes: they\nreceive two projectors' worth of leaked black. The fix cannot darken\nthe seam, so it lifts the signal everywhere *else* to match —\n`out = lift + (1 − lift)·in`. On a black scene, raise this until the\nun-doubled regions match the seams."
              }
            ],
            "default": 0.0
          },
          "blend": {
            "type": "boolean",
            "description": "Master switch for seam ramps. `false` retains required slicing and warp\nprocessing but disables the transfer fades.",
            "default": true
          },
          "canvas": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/CanvasConfig",
                "description": "Shared canvas dimensions for Simple, Warp, and capability fallback."
              }
            ],
            "default": null
          },
          "freeRun": {
            "type": "boolean",
            "description": "Let each output take frames at its own pace.\n\nOff (the default), the slicer commits a frame to every output together\nand does not commit the next until all of them have taken it, so the\nsame frame is on every display at once. On, each output is handed the\nnewest frame the moment it is ready for one: displays at different\nrefresh rates each run at their own, and the wall gives up being in\nstep. Only for installations that cannot share a rate.",
            "default": false
          },
          "gamma": {
            "type": "number",
            "format": "double",
            "description": "The projectors' transfer gamma, shaping every ramp's fall-off.\n\nA ramp that is linear in signal is not linear in light: the display\nraises the signal to `gamma`. Each ramp is therefore pre-shaped as\n`ramp^(1/gamma)` so that the *luminance* of the two overlapping\nprojectors sums to a constant across the seam. One value for the whole\ninstallation — these are near-universally identical projectors; per-output\noverrides can be added later if mixed models ever matter.",
            "default": 2.2
          },
          "mode": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/ProjectionMode",
                "description": "Which geometry pipeline is requested. Warp settings remain retained\nwhile simple mode is active."
              }
            ],
            "default": "simple"
          },
          "renderer": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Renderer",
                "description": "Which pipeline the slicer composites with; see [`Renderer`]."
              }
            ],
            "default": "auto"
          },
          "temporary": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/TemporarySettings",
                "description": "Ephemeral settings applied to the working copy like any other field,\nbut never persisted. See [`TemporarySettings`]."
              }
            ],
            "default": {
              "highlightOverlaps": false
            }
          },
          "testPattern": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/TestPattern",
                "description": "Show a built-in test pattern instead of the content. `null` is off.\n\nPatterns draw in *global* coordinates, so features continue exactly\nacross a seam — two aligned projectors superimpose them perfectly.\nThey are the bench-verification and field-alignment tool: the blend\nramps and black lift still apply on top, exactly as they would to\nreal content."
              }
            ],
            "default": null
          }
        },
        "additionalProperties": false
      },
      "ProjectionConfigGenerations": {
        "type": "object",
        "description": "Desired-state document generations this slicer session has processed.\nNever comparable with [`ProjectionChildGenerations`]'s child-local\ncontrol sequence.",
        "properties": {
          "applied": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "description": "Working-copy generation whose projection snapshot the slicer has\ninstalled. It advances only on an `applied` event from the current\nslicer session, or when an unchanged effective snapshot is already\nknown to have been installed.",
            "minimum": 0
          },
          "requested": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "description": "Working-copy generation whose effective projection snapshot the\nmanager most recently asked this slicer session to realize.",
            "minimum": 0
          }
        }
      },
      "ProjectionControlFailure": {
        "type": "object",
        "description": "A rejected revision or a closed control channel, retained until a newer\nsuccessful update supersedes it.",
        "required": [
          "reason"
        ],
        "properties": {
          "generation": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "minimum": 0
          },
          "reason": {
            "type": "string"
          }
        }
      },
      "ProjectionControlOutputStatus": {
        "type": "object",
        "description": "The latest lifecycle generation reported for one named output.",
        "required": [
          "name"
        ],
        "properties": {
          "appliedGeneration": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "minimum": 0
          },
          "builtGeneration": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "minimum": 0
          },
          "name": {
            "type": "string"
          },
          "presentedGeneration": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "minimum": 0
          },
          "samplingMode": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/SamplingMode",
                "description": "Filled when the slicer reports its effective sampling path."
              }
            ]
          },
          "submittedGeneration": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "minimum": 0
          }
        }
      },
      "ProjectionControlStatus": {
        "type": "object",
        "description": "Observed progress of the complete snapshots sent to the slicer.",
        "properties": {
          "blackLift": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/ProjectionBlackLiftStatus",
                "description": "Shared source measurement and lift for the active slicer session."
              }
            ]
          },
          "buildMs": {
            "type": [
              "number",
              "null"
            ],
            "format": "double"
          },
          "childGeneration": {
            "$ref": "#/components/schemas/ProjectionChildGenerations",
            "description": "Control-protocol generation counters, local to the current child\nsession's own sequence."
          },
          "configGeneration": {
            "$ref": "#/components/schemas/ProjectionConfigGenerations",
            "description": "Desired-state document generations this slicer session has\nprocessed."
          },
          "effectiveMode": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/ProjectionMode"
              }
            ]
          },
          "effectiveRenderer": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/Renderer"
              }
            ]
          },
          "lastFailure": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/ProjectionControlFailure"
              }
            ]
          },
          "outputs": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ProjectionControlOutputStatus"
            },
            "description": "Per-output lifecycle prevents global submitted/presented values from\nimplying an atomic wall-wide flip."
          },
          "requestedMode": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/ProjectionMode",
                "description": "Requested operator mode and the mode the negotiated pipeline runs, as\nreported by the child's own `Capability` event. This is the child's\nself-report, not the public policy answer: see\n[`ProjectionGeometryStatus::effective_mode`] for the one authoritative\neffective mode a client should read, which also accounts for feature\ngating, `allowOverlaps`, and capability-probe hysteresis this field\nknows nothing about."
              }
            ]
          },
          "requestedRenderer": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/Renderer",
                "description": "Renderer requested by configuration and renderer actually negotiated\nby the running child. These are separate because Auto may fall back."
              }
            ]
          },
          "session": {
            "type": [
              "string",
              "null"
            ],
            "description": "Session of the currently running child, when it has accepted a live\ncontrol message."
          },
          "uploadMs": {
            "type": [
              "number",
              "null"
            ],
            "format": "double"
          },
          "warpAvailable": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Whether the selected capture/presentation pipeline can apply a warp."
          },
          "warpReason": {
            "type": [
              "string",
              "null"
            ],
            "description": "Why warp is unavailable, including a known remediation when the child\ncan provide one."
          }
        }
      },
      "ProjectionGeometryStatus": {
        "type": "object",
        "required": [
          "requestedRenderer",
          "requestedMode",
          "effectiveMode",
          "retainedWarp"
        ],
        "properties": {
          "effectiveMode": {
            "$ref": "#/components/schemas/ProjectionMode"
          },
          "reason": {
            "type": [
              "string",
              "null"
            ]
          },
          "requestedMode": {
            "$ref": "#/components/schemas/ProjectionMode"
          },
          "requestedRenderer": {
            "$ref": "#/components/schemas/Renderer"
          },
          "retainedWarp": {
            "type": "boolean"
          },
          "warpAvailable": {
            "type": [
              "boolean",
              "null"
            ]
          }
        }
      },
      "ProjectionMode": {
        "type": "string",
        "description": "The selected projection pipeline.",
        "enum": [
          "simple",
          "warp"
        ]
      },
      "ProjectionReport": {
        "type": "object",
        "description": "What the projection pipeline is doing right now, as served by `GET\n/projection/stats` and `projection_stats_changed`.\n\nAdded 2026-09-15: a four-projector bench had a slicer that was\ndemonstrably alive (`pgrep -f \"suede slice\"` found it, and it had logged\nits startup line) but had produced no frames, because the frame loop is\ndamage-driven and the active page was a static image. The endpoint used\nto be `ProjectionStats | null`, and that `null` was reported for \"no\nslicer at all\" and \"slicer running but silent\" alike — which is exactly\nhow a running slicer got diagnosed as not running. `running` is tracked\nseparately from whatever the slicer has or has not reported, so the two\nsituations are no longer the same value.",
        "required": [
          "running"
        ],
        "properties": {
          "control": {
            "$ref": "#/components/schemas/ProjectionControlStatus",
            "description": "Lifecycle of the most recent live slicer update.  This is independent\nof frame-interval statistics: a static canvas can apply a revision\nbefore it has any ten-second interval to report."
          },
          "geometry": {
            "$ref": "#/components/schemas/ProjectionGeometryStatus",
            "description": "Public mode selection, independent of sampler-level child diagnostics."
          },
          "lastInterval": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/ProjectionStats",
                "description": "The last completed reporting interval, or `null` when none has been\nproduced yet."
              }
            ]
          },
          "running": {
            "type": "boolean",
            "description": "Whether a slicer process is alive. A slicer can be running and yet\nhave reported nothing: the frame loop is damage-driven, so a static\npage produces no frames and therefore no interval. Distinguishing\nthat from \"no slicer at all\" is the point of this field — the two\nwere indistinguishable until a bench reported \"the slicer is not\nrunning\" about a slicer that was running."
          }
        }
      },
      "ProjectionStats": {
        "type": "object",
        "description": "What the slicer measured over its last reporting interval.\n\nReported by the slicer subprocess as a JSON line on its stdout every ten\nseconds (see `crate::projection::slicer`), read by the manager and held\nas [`ProjectionReport::last_interval`]. `None` there means no interval\nhas completed yet, which is normal for a slicer that is running but has\nnothing to capture — see [`ProjectionReport::running`].",
        "required": [
          "measuredAt",
          "intervalSeconds",
          "freeRun",
          "canvasFps",
          "presentedFps",
          "framesSuperseded",
          "stalls",
          "perFrameMs",
          "presentationFeedback",
          "straddles",
          "gateHolds",
          "renderer",
          "captureIntervals",
          "outputs"
        ],
        "properties": {
          "canvasFps": {
            "type": "number",
            "format": "double",
            "description": "Canvas frames captured per second."
          },
          "captureIntervals": {
            "$ref": "#/components/schemas/CaptureIntervals",
            "description": "How many canvas periods elapsed between successive captures reaching\nthe slicer, bucketed. Says directly whether the capture loop is\nkeeping up with every canvas frame or only every second one."
          },
          "framesSuperseded": {
            "type": "integer",
            "format": "int32",
            "description": "Captured frames replaced by a newer one before any output showed them.",
            "minimum": 0
          },
          "freeRun": {
            "type": "boolean",
            "description": "Whether outputs were taking frames at their own pace (see ProjectionConfig.free_run)."
          },
          "gateHolds": {
            "type": "integer",
            "format": "int32",
            "description": "Commit cycles the gate held past one canvas period waiting for an\noutput that had not yet reported presenting the previous frame — the\nother outputs repeated a frame rather than move on without it. Zero\non a healthy wall, where every output's presentation feedback is back\nwell before the next frame is due; a rising count means one head's\nflips are landing a refresh later than the rest, and names the cost\nof keeping the wall together rather than a fault in it.",
            "minimum": 0
          },
          "intervalSeconds": {
            "type": "number",
            "format": "double"
          },
          "measuredAt": {
            "type": "integer",
            "format": "int64",
            "description": "Unix seconds at the end of the interval.",
            "minimum": 0
          },
          "offsetMs": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/PresentationOffset",
                "description": "Spread between the earliest and latest output to present the same frame.\nNone when fewer than two outputs reported a frame this interval."
              }
            ]
          },
          "outputs": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OutputTiming"
            }
          },
          "perFrameMs": {
            "$ref": "#/components/schemas/FrameCost"
          },
          "presentationBackend": {
            "type": [
              "string",
              "null"
            ],
            "description": "Presentation backend used for this interval: `wayland` or `vulkan-display`.\nAbsent in reports from older slicers."
          },
          "presentationFeedback": {
            "type": "boolean",
            "description": "Whether the backend supplies per-image presentation feedback. Completion\ncan be known even when its timestamp is unavailable; missing timestamp\nmeasurements remain null, not zero."
          },
          "presentedFps": {
            "type": "number",
            "format": "double",
            "description": "Present cycles per second. Locked: one per all-output commit. Free: one per\nsnapshot that reached at least one output."
          },
          "renderer": {
            "type": "string",
            "description": "Which backend this interval's frames were blended on: `\"cpu\"` or `\"gpu\"`."
          },
          "stalls": {
            "type": "integer",
            "format": "int32",
            "description": "Times an output stopped answering frame callbacks and was dropped from the gate.",
            "minimum": 0
          },
          "straddles": {
            "type": "integer",
            "format": "int32",
            "description": "Frames whose outputs presented more than half a refresh period apart —\ni.e. shown on different refreshes, a whole-frame mismatch.",
            "minimum": 0
          },
          "timestampSource": {
            "type": [
              "string",
              "null"
            ],
            "description": "Clock and protocol used to timestamp displayed frames, when available."
          }
        }
      },
      "ReadinessConfig": {
        "type": "object",
        "description": "Wait for a URL to answer before launching an application.\n\nA kiosk browser started before the service it points at is serving shows an\nerror page and stays there, since nothing reloads it. Gating the launch on\nthe service answering removes that race entirely.",
        "required": [
          "url"
        ],
        "properties": {
          "expectStatus": {
            "type": "array",
            "items": {
              "type": "integer",
              "format": "int32",
              "minimum": 0
            },
            "description": "Status codes that mean ready. Empty means any 2xx."
          },
          "giveUpAfterSeconds": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "description": "Give up waiting after this long and launch anyway. `null` waits forever,\nwhich is usually right for an appliance: showing an error page is worse\nthan showing the background until the service appears.",
            "minimum": 0
          },
          "intervalSeconds": {
            "type": "integer",
            "format": "int64",
            "description": "How long between attempts.",
            "minimum": 0
          },
          "timeoutSeconds": {
            "type": "integer",
            "format": "int64",
            "description": "How long a single attempt may take.",
            "minimum": 0
          },
          "url": {
            "type": "string",
            "description": "URL to poll. Only `http://` is supported."
          }
        },
        "additionalProperties": false
      },
      "RecommendationResponse": {
        "type": "object",
        "description": "Read-only resolution guidance. `approximate` is always true: the sampled\nJacobian maximum plus an engineering margin is guidance, not a proof of the\nexact maximum density.",
        "required": [
          "revision",
          "generation",
          "requestedAspect",
          "idealWidth",
          "idealHeight",
          "admissibleWidth",
          "admissibleHeight",
          "presets",
          "knownLimits",
          "unknownLimits",
          "warnings",
          "approximate"
        ],
        "properties": {
          "admissibleHeight": {
            "type": "integer",
            "format": "int32",
            "minimum": 0
          },
          "admissibleWidth": {
            "type": "integer",
            "format": "int32",
            "minimum": 0
          },
          "approximate": {
            "type": "boolean"
          },
          "generation": {
            "type": "integer",
            "format": "int64",
            "minimum": 0
          },
          "idealHeight": {
            "type": "integer",
            "format": "int64",
            "minimum": 0
          },
          "idealWidth": {
            "type": "integer",
            "format": "int64",
            "minimum": 0
          },
          "knownLimits": {
            "$ref": "#/components/schemas/ResolutionLimits"
          },
          "presets": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ScalePreset"
            }
          },
          "requestedAspect": {
            "type": "number",
            "format": "double"
          },
          "revision": {
            "type": "integer",
            "format": "int64",
            "minimum": 0
          },
          "unknownLimits": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "Rect": {
        "type": "object",
        "required": [
          "x",
          "y",
          "width",
          "height"
        ],
        "properties": {
          "height": {
            "type": "integer",
            "format": "int32"
          },
          "width": {
            "type": "integer",
            "format": "int32"
          },
          "x": {
            "type": "integer",
            "format": "int32"
          },
          "y": {
            "type": "integer",
            "format": "int32"
          }
        }
      },
      "Renderer": {
        "type": "string",
        "description": "Which pipeline the slicer uses to composite the canvas onto each output.\n\n`Auto` (the default) prefers the GPU path — the compositor blits the\ncanvas into a Vulkan image exported as a dmabuf, a fragment shader blends\nit straight into each output's own dmabuf, and no pixel ever crosses to\nsystem memory — falling back to the CPU path (shared-memory screencopy,\nblended on the CPU) whenever the compositor does not offer dmabuf capture\nor Vulkan fails to initialize. `Cpu` forces the fallback path even on\nhardware that could do better. `Gpu` forces the GPU path and is a startup\nerror if the machine cannot actually provide it — the slicer exits and\nthe daemon respawns it on its next reconcile, rather than silently\nrunning the slower path — for a rig where that would go unnoticed.",
        "enum": [
          "auto",
          "cpu",
          "gpu"
        ]
      },
      "ResolutionLimits": {
        "type": "object",
        "description": "Known allocation limits used by the recommendation calculation.",
        "required": [
          "maxDimension",
          "maxCanvasPixels",
          "maxOutputPixels"
        ],
        "properties": {
          "maxCanvasPixels": {
            "type": "integer",
            "format": "int64",
            "minimum": 0
          },
          "maxDimension": {
            "type": "integer",
            "format": "int32",
            "minimum": 0
          },
          "maxOutputPixels": {
            "type": "integer",
            "format": "int64",
            "minimum": 0
          }
        }
      },
      "RestartPolicy": {
        "type": "object",
        "required": [
          "policy",
          "delayMs",
          "maxDelayMs"
        ],
        "properties": {
          "delayMs": {
            "type": "integer",
            "format": "int64",
            "description": "Initial delay before relaunching.",
            "minimum": 0
          },
          "maxDelayMs": {
            "type": "integer",
            "format": "int64",
            "description": "Ceiling for the exponential backoff.",
            "minimum": 0
          },
          "policy": {
            "$ref": "#/components/schemas/RestartPolicyKind"
          }
        },
        "additionalProperties": false
      },
      "RestartPolicyKind": {
        "type": "string",
        "description": "Restart behavior after an application exits.",
        "enum": [
          "always",
          "on-failure",
          "never"
        ]
      },
      "RestartReason": {
        "type": "string",
        "description": "Why an app was last restarted.",
        "enum": [
          "processExited",
          "heartbeatTimeout",
          "windowNeverAppeared",
          "configChanged",
          "apiRequest"
        ]
      },
      "SamplingMode": {
        "type": "string",
        "description": "How a slicer output actually sampled its captured source.\n\nDistinct from [`ProjectionMode`]: a Simple-mode crop that happens to sit\non an integer pixel boundary samples `Exact` just like an identity Warp\noutput would, while a fractional crop or corner pin in either mode needs\n`Bilinear`. This is a report of the sampling path taken, not of which\npipeline requested it.",
        "enum": [
          "exact",
          "bilinear"
        ]
      },
      "ScalePreset": {
        "type": "object",
        "description": "A UI-adoptable resolution preset.",
        "required": [
          "scale",
          "width",
          "height",
          "achievedScale",
          "available"
        ],
        "properties": {
          "achievedScale": {
            "type": "number",
            "format": "double"
          },
          "available": {
            "type": "boolean",
            "description": "Whether this target can be allocated within the known canvas limits.\nUnavailable targets remain in the response so the UI can explain and\ndisable them instead of relabeling a clamped width as 100%."
          },
          "height": {
            "type": "integer",
            "format": "int64",
            "minimum": 0
          },
          "scale": {
            "type": "number",
            "format": "double"
          },
          "width": {
            "type": "integer",
            "format": "int64",
            "minimum": 0
          }
        }
      },
      "Settings": {
        "type": "object",
        "description": "Daemon-level settings that belong to desired state.",
        "required": [
          "hideCursor",
          "outputPollIntervalSeconds"
        ],
        "properties": {
          "hideCursor": {
            "type": "boolean",
            "description": "Hide the pointer and park it beyond the layout."
          },
          "measureCapabilitiesOnStart": {
            "type": "boolean",
            "description": "Measure browser decode capabilities at startup when the browser,\nits configuration, or the GPU driver changed since last measured.\nA brief window opens on the displays while it runs; with nothing\nchanged, nothing opens."
          },
          "outputPollIntervalSeconds": {
            "type": "integer",
            "format": "int64",
            "description": "Backstop poll interval for output changes.",
            "minimum": 0
          }
        },
        "additionalProperties": false
      },
      "Status": {
        "type": "object",
        "description": "Reconciliation status, as served by `GET /status`.\n\nAnswers \"is this appliance doing what I asked, and will it still be after\na reboot\" from one call: `state` and `divergences` alone say whether\ndesired state is fulfilled *right now*, but say nothing about whether the\ndocument behind that answer survives a restart, whether the machine has\ncaught up with a write still in flight, or whether the environment it\ndepends on is otherwise healthy. See `docs/specification.md`'s `/status`\nsection for the predicate spelled out for client authors.",
        "required": [
          "state",
          "divergences",
          "revision",
          "committed",
          "currentRevision"
        ],
        "properties": {
          "activeApp": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/ActiveApp",
                "description": "The app `activeApp` names, and how it is doing, so \"is my app on the\nscreens\" is one call: `state == running` here beside `synced` above.\n`None` when no app is active. Full detail (pid, restarts, window ids)\nstays on `GET /apps/{id}/status`."
              }
            ]
          },
          "checks": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/CheckSummary",
                "description": "How the environment health checks last came out, by status.\n\nDivergences and checks are different axes: a machine can apply its\nconfiguration perfectly while its browser is missing hardware video\ndecode. Summarized here so the common question takes one call; the\ndetail stays at `GET /system/checks`.\n\n`None` rather than an all-zero [`CheckSummary`] wherever the counts\nare not actually known. A reconciliation pass never runs the checks\nitself — they shell out to other programs, and a pass must stay\ncheap — so the `Status` it publishes over SSE cannot honestly fill\nthis in; a zeroed summary there would read as \"everything passed\",\nwhich is a claim nobody made. `GET /status` always fills it in from\nthe check runner's last results, because it has one to ask."
              }
            ]
          },
          "committed": {
            "type": "boolean",
            "description": "Whether the document the last pass applied was the saved one.\n\nA working copy is applied exactly as a saved document is, so an\nappliance can be `synced` against a document that will vanish on\nrestart. A client asking \"will it still look like this tomorrow\"\nneeds this, and it is not otherwise visible without fetching the\nconfiguration too."
          },
          "currentRevision": {
            "type": "integer",
            "format": "int64",
            "description": "The revision of the desired-state document right now.\n\n`revision` is the one the last pass *applied*; these differ while a\nwrite is still being reconciled. Equal values mean the machine has\ncaught up with the last write, which is the question a client asks\nafter a PUT.",
            "minimum": 0
          },
          "divergences": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Divergence"
            }
          },
          "lastReconciled": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "description": "Unix seconds of the last completed reconciliation pass.",
            "minimum": 0
          },
          "revision": {
            "type": "integer",
            "format": "int64",
            "description": "Desired-state revision the last pass applied.",
            "minimum": 0
          },
          "state": {
            "$ref": "#/components/schemas/SyncState"
          }
        }
      },
      "SwayCommand": {
        "type": "object",
        "description": "A raw Sway command, for debugging.",
        "required": [
          "command"
        ],
        "properties": {
          "command": {
            "type": "string"
          }
        }
      },
      "SyncState": {
        "type": "string",
        "description": "Overall reconciliation state.",
        "enum": [
          "synced",
          "reconciling",
          "degraded"
        ]
      },
      "SystemInfo": {
        "type": "object",
        "description": "Daemon and environment information, as served by `GET /system`.",
        "required": [
          "suedeVersion",
          "buildId",
          "uptimeSeconds",
          "packages",
          "supportsTearing",
          "webUiEnabled",
          "powerVerbs",
          "presentation",
          "glYield",
          "outputAlignment"
        ],
        "properties": {
          "buildId": {
            "type": "string",
            "description": "Which build is running: `git describe` output, e.g.\n`v0.1.0-12-g81226ee`. `suedeVersion` names the release this is meant\nto be; this names the commit it was actually built from, which is the\nquestion when a fix appears not to have landed."
          },
          "glYield": {
            "$ref": "#/components/schemas/GlYieldStatus",
            "description": "NVIDIA's `__GL_YIELD` toggle, requested and live — experimental; see\n[`GlYieldStatus`]."
          },
          "hostname": {
            "type": [
              "string",
              "null"
            ]
          },
          "nvidiaDriver": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/NvidiaDriverStatus",
                "description": "The NVIDIA driver this session runs, or `null` on a non-NVIDIA\nmachine — see [`crate::nvidia_driver::detect`]. Read fresh on every\nrequest, like `gl_yield.effective`."
              }
            ]
          },
          "outputAlignment": {
            "$ref": "#/components/schemas/OutputAlignmentStatus",
            "description": "Automatic output phase alignment this compositor session — see\n[`OutputAlignmentStatus`] and\n[`crate::config::BootstrapConfig::align_outputs`]."
          },
          "packages": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PackageVersion"
            }
          },
          "powerVerbs": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PowerVerb"
            },
            "description": "Host power operations this appliance permits. Empty means none; the web\nUI uses it to disable and explain its buttons rather than offering ones\nthat would be refused."
          },
          "presentation": {
            "$ref": "#/components/schemas/PresentationStatus",
            "description": "How this session was asked to present, and how it actually is —\nexperimental; see\n[`crate::config::BootstrapConfig::presentation_effective`]."
          },
          "suedeVersion": {
            "type": "string"
          },
          "supportsTearing": {
            "type": "boolean",
            "description": "Feature gates resolved from the detected Sway version."
          },
          "swayVersion": {
            "type": [
              "string",
              "null"
            ]
          },
          "uptimeSeconds": {
            "type": "integer",
            "format": "int64",
            "description": "Seconds since the daemon started.",
            "minimum": 0
          },
          "webUiEnabled": {
            "type": "boolean",
            "description": "True when the reference web UI is being served."
          }
        }
      },
      "TemporarySettings": {
        "type": "object",
        "description": "Ephemeral, never-persisted settings: applied to the working copy exactly\nlike any other field, but reset to default wherever a document is about\nto reach disk.\n\nThat reset is structural, not a client convention: `commit_if` and\n`commit_literal_if` (`src/api/mod.rs`) reset this struct to its default,\nunconditionally, right before their `stage_replace_if` closures return —\nso it is categorically impossible for any caller, this UI or any future\nor API-direct one, to persist a non-default value here. The other reset\npoints fall out of that same guarantee for free: cancel\n(`clear_preview_if` discards the whole preview, the only place this\ncould be non-default), daemon startup/disk load (the persisted document\ncan never contain a non-default value, per the commit-time reset), and\nan explicit toggle back off (an ordinary write like any other).\n\nThis is the intended home for future ephemeral, render-affecting\ntoggles — debug overlays and the like — that only make sense on the live\nworking copy. `test_pattern` above predates this struct and stays where\nit is (moving it would be a breaking rename of an established field); it\nis only *conventionally* non-persistent today, relying on a well-behaved\nclient to strip it before every commit. New fields of this kind belong\nhere instead, where the guarantee is enforced rather than assumed.",
        "properties": {
          "highlightOverlaps": {
            "type": "boolean",
            "description": "Overlay two-pixel orange/blue seam-boundary marker lines on every\nprojector output, to aid warp lineup. Off by default, and meaningful\nonly while edge blending (`blend`) is on.",
            "default": false
          }
        },
        "additionalProperties": false
      },
      "TestPattern": {
        "type": "string",
        "description": "A built-in projection test pattern.",
        "enum": [
          "grid",
          "white",
          "black",
          "gamma",
          "identify",
          "sync",
          "warp-alignment"
        ]
      },
      "Transform": {
        "type": "string",
        "description": "Output rotation and flipping, as accepted by `sway-output(5)`.",
        "enum": [
          "normal",
          "90",
          "180",
          "270",
          "flipped",
          "flipped-90",
          "flipped-180",
          "flipped-270"
        ]
      },
      "UnusedCanvas": {
        "type": "object",
        "description": "The fraction of the canvas left uncovered on each axis, measured before\nany [`super::desired::OutputConfig::arrange_offset`] is applied.\n\nZero in overlap mode unless [`ArrangementRequest::allow_unused_canvas`]\nasked for the fit-inside scale: the default `min`-of-fits scale fills the\naxis with the smaller fit exactly (up to float dust) and overhangs the\nother, so neither falls short. With `allowUnusedCanvas`, the\n`max`-of-fits scale fills the axis with the larger fit instead and leaves\nthe other short. In content-scale mode an axis without a seam (a single\nrow or column) can fall short at the asked-for scale, which [`solve`]\nrefuses unless `allowUnusedCanvas` says to accept it. Whichever axis is\nshort is centered on the canvas, so half of its value is left before the\nfirst slot and half after the last.",
        "required": [
          "x",
          "y"
        ],
        "properties": {
          "x": {
            "type": "number",
            "format": "double"
          },
          "y": {
            "type": "number",
            "format": "double"
          }
        }
      },
      "VideoSource": {
        "type": "object",
        "description": "A video input (capture) device, surfaced via WirePlumber's v4l2 monitor.",
        "required": [
          "id"
        ],
        "properties": {
          "card": {
            "type": [
              "string",
              "null"
            ],
            "description": "V4L2 card string (`api.v4l2.cap.card`). This, not `description`, is\nwhat a Chromium kiosk's `enumerateDevices` labels the device as,\nsince video devices are opened straight through V4L2."
          },
          "description": {
            "type": [
              "string",
              "null"
            ],
            "description": "Human-readable `node.description`."
          },
          "id": {
            "type": "string",
            "description": "PipeWire `node.name` — stable across reboots and replugging."
          },
          "path": {
            "type": [
              "string",
              "null"
            ],
            "description": "V4L2 device path (`api.v4l2.path`), e.g. `/dev/video0`."
          }
        }
      },
      "Wallpaper": {
        "type": "object",
        "description": "Metadata for a stored wallpaper, as served by the API.",
        "required": [
          "id",
          "contentType",
          "bytes",
          "uploadedAt"
        ],
        "properties": {
          "bytes": {
            "type": "integer",
            "format": "int64",
            "minimum": 0
          },
          "contentType": {
            "type": "string",
            "description": "`image/png` or `image/jpeg`."
          },
          "id": {
            "type": "string",
            "description": "Client-chosen identifier, referenced from an output's background."
          },
          "uploadedAt": {
            "type": "integer",
            "format": "int64",
            "description": "Unix seconds when it was stored.",
            "minimum": 0
          }
        }
      },
      "Window": {
        "type": "object",
        "description": "A window in Sway's tree.",
        "required": [
          "id",
          "fullscreenMode",
          "rect"
        ],
        "properties": {
          "app": {
            "type": [
              "string",
              "null"
            ],
            "description": "Id of the Suede-managed app that owns this window, where known."
          },
          "appId": {
            "type": [
              "string",
              "null"
            ]
          },
          "fullscreenMode": {
            "type": "integer",
            "format": "int32"
          },
          "id": {
            "type": "integer",
            "format": "int64",
            "description": "Sway container id."
          },
          "output": {
            "type": [
              "string",
              "null"
            ],
            "description": "Name of the output this window is displayed on, where known."
          },
          "pid": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int32"
          },
          "rect": {
            "$ref": "#/components/schemas/Rect"
          },
          "title": {
            "type": [
              "string",
              "null"
            ]
          },
          "visible": {
            "type": [
              "boolean",
              "null"
            ]
          }
        }
      },
      "WindowChange": {
        "type": "object",
        "description": "Payload of the `windows_changed` event.",
        "required": [
          "change",
          "window"
        ],
        "properties": {
          "change": {
            "type": "string",
            "description": "Sway change type: `new`, `close`, `title`, `move`, `fullscreen_mode`, `floating`."
          },
          "window": {
            "$ref": "#/components/schemas/Window"
          }
        }
      }
    },
    "securitySchemes": {
      "bearer": {
        "type": "http",
        "scheme": "bearer",
        "description": "Optional. When a token is configured, every endpoint except /healthz and the heartbeat endpoint requires it."
      }
    }
  },
  "tags": [
    {
      "name": "observed",
      "description": "Live state reported by sway and PipeWire"
    },
    {
      "name": "config",
      "description": "Desired state, persisted and reconciled"
    },
    {
      "name": "apps",
      "description": "Managed application status and control"
    },
    {
      "name": "control",
      "description": "Imperative escape hatches"
    },
    {
      "name": "events",
      "description": "Server-sent change notification"
    },
    {
      "name": "wallpapers",
      "description": "Images shown when no window covers an output"
    }
  ]
}
