{
  "$defs": {
    "AppOverride": {
      "additionalProperties": false,
      "properties": {
        "command": {
          "items": {
            "type": "string"
          },
          "type": [
            "array",
            "null"
          ]
        },
        "enabled": {
          "type": [
            "boolean",
            "null"
          ]
        },
        "env": {
          "additionalProperties": {
            "type": [
              "string",
              "null"
            ]
          },
          "description": "Merged into the app's environment; `null` removes a variable.",
          "type": "object"
        },
        "files": {
          "additionalProperties": {
            "anyOf": [
              {
                "$ref": "#/$defs/FileSpec"
              },
              {
                "type": "null"
              }
            ]
          },
          "description": "Merged into the app's files; `null` removes a file.",
          "type": "object"
        },
        "image": {
          "type": [
            "string",
            "null"
          ]
        }
      },
      "type": "object"
    },
    "AppSpec": {
      "additionalProperties": false,
      "properties": {
        "command": {
          "items": {
            "type": "string"
          },
          "type": [
            "array",
            "null"
          ]
        },
        "devices": {
          "items": {
            "type": "string"
          },
          "type": "array"
        },
        "enabled": {
          "description": "Leave the app out unless a site or device override enables it.",
          "type": "boolean"
        },
        "env": {
          "additionalProperties": {
            "type": "string"
          },
          "type": "object"
        },
        "files": {
          "additionalProperties": {
            "$ref": "#/$defs/FileSpec"
          },
          "description": "Configuration files by absolute container path. The CLI reads\n`source:` files relative to `suta.yaml` and sends their content.",
          "type": "object"
        },
        "image": {
          "description": "`registry/repository:tag` or `…@sha256:…`; Docker Hub names may be short.",
          "type": "string"
        },
        "network": {
          "$ref": "#/$defs/Network"
        },
        "ports": {
          "description": "`HOST:CONTAINER` or `HOST:CONTAINER/udp`.",
          "items": {
            "type": "string"
          },
          "type": "array"
        },
        "privileged": {
          "type": "boolean"
        },
        "probe": {
          "anyOf": [
            {
              "$ref": "#/$defs/ProbeSpec"
            },
            {
              "type": "null"
            }
          ]
        },
        "secrets": {
          "items": {
            "$ref": "#/$defs/SecretSpec"
          },
          "type": "array"
        },
        "volumes": {
          "additionalProperties": {
            "type": "string"
          },
          "description": "Container path → volume name, with `:ro` for read-only.",
          "type": "object"
        }
      },
      "required": [
        "image"
      ],
      "type": "object"
    },
    "Duration": {
      "anyOf": [
        {
          "format": "uint32",
          "maximum": 3600,
          "minimum": 10,
          "type": "integer"
        },
        {
          "pattern": "^\\s*0*(([1-9][0-9]{1,2}|[1-2][0-9]{3}|3[0-5][0-9]{2}|3600)s?|([1-9]|[1-5][0-9]|60)m|1h)\\s*$",
          "type": "string"
        }
      ],
      "description": "Seconds, or a number with `s`, `m` or `h`, such as `90s`, `2m` or `1h`."
    },
    "FileSpec": {
      "additionalProperties": false,
      "properties": {
        "content": {
          "type": [
            "string",
            "null"
          ]
        },
        "mode": {
          "description": "Octal mode such as `0644` (the default) or `0600`.",
          "type": [
            "string",
            "null"
          ]
        },
        "source": {
          "description": "A path relative to `suta.yaml`; the CLI replaces it with `content`.",
          "type": [
            "string",
            "null"
          ]
        }
      },
      "type": "object"
    },
    "HardwareSpec": {
      "additionalProperties": false,
      "description": "Boot-time hardware settings, applied through `config.txt` when the\ndevice restarts. Unset settings keep the value from the layer below.",
      "properties": {
        "i2c": {
          "description": "Turn on the I2C interface on the GPIO header (`/dev/i2c-1`).",
          "type": [
            "boolean",
            "null"
          ]
        },
        "overlays": {
          "description": "Device-tree overlays in load order, each written as after `dtoverlay=`\nin `config.txt`, such as `hifiberry-dac` or `gpio-fan,gpiopin=14`.",
          "items": {
            "pattern": "^[a-z0-9][a-z0-9_-]{0,63}(,[A-Za-z0-9_][A-Za-z0-9_-]{0,63}(=[A-Za-z0-9][A-Za-z0-9_.:+-]{0,63})?){0,16}$",
            "type": "string"
          },
          "type": [
            "array",
            "null"
          ]
        },
        "spi": {
          "description": "Turn on the SPI interface (`/dev/spidev0.0`, `/dev/spidev0.1`).",
          "type": [
            "boolean",
            "null"
          ]
        }
      },
      "type": "object"
    },
    "Network": {
      "oneOf": [
        {
          "const": "bridge",
          "description": "Private network with published ports.",
          "type": "string"
        },
        {
          "const": "host",
          "description": "The device's own network stack, for discovery protocols and hardware.",
          "type": "string"
        }
      ]
    },
    "Override": {
      "additionalProperties": false,
      "properties": {
        "apps": {
          "additionalProperties": {
            "$ref": "#/$defs/AppOverride"
          },
          "type": "object"
        },
        "hardware": {
          "$ref": "#/$defs/HardwareSpec",
          "description": "Replaces the settings it names; `overlays` replaces the whole list."
        },
        "os": {
          "type": [
            "string",
            "null"
          ]
        },
        "probation": {
          "anyOf": [
            {
              "$ref": "#/$defs/Duration"
            },
            {
              "type": "null"
            }
          ]
        }
      },
      "type": "object"
    },
    "ProbeSpec": {
      "additionalProperties": false,
      "description": "Exactly one check: `http: /health` with `port`, `tcp: 5432`, or\n`exec: [pg_isready]`.",
      "properties": {
        "exec": {
          "description": "A command that exits 0 inside the container.",
          "items": {
            "type": "string"
          },
          "type": [
            "array",
            "null"
          ]
        },
        "http": {
          "description": "Path answered with 2xx or 3xx by `GET http://127.0.0.1:<port><path>`.",
          "type": [
            "string",
            "null"
          ]
        },
        "port": {
          "description": "The container port for an HTTP probe.",
          "format": "uint16",
          "maximum": 65535,
          "minimum": 0,
          "type": [
            "integer",
            "null"
          ]
        },
        "tcp": {
          "description": "A container port that accepts TCP connections.",
          "format": "uint16",
          "maximum": 65535,
          "minimum": 0,
          "type": [
            "integer",
            "null"
          ]
        }
      },
      "type": "object"
    },
    "RegistrySpec": {
      "additionalProperties": false,
      "properties": {
        "password": {
          "description": "The name of the Suta secret holding the password or token.",
          "type": "string"
        },
        "username": {
          "type": "string"
        }
      },
      "required": [
        "username",
        "password"
      ],
      "type": "object"
    },
    "SecretSpec": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "properties": {
            "env": {
              "type": [
                "string",
                "null"
              ]
            },
            "name": {
              "type": "string"
            }
          },
          "required": [
            "name"
          ],
          "type": "object"
        }
      ],
      "description": "`name` (the latest version when planned), `name@3`, or `{name, env}`."
    }
  },
  "$id": "https://suta.dev/schema/suta.json",
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "additionalProperties": false,
  "properties": {
    "apps": {
      "additionalProperties": {
        "$ref": "#/$defs/AppSpec"
      },
      "default": {},
      "type": "object"
    },
    "devices": {
      "additionalProperties": {
        "$ref": "#/$defs/Override"
      },
      "description": "Overrides for one device, by name; applied after its site's.",
      "type": "object"
    },
    "hardware": {
      "$ref": "#/$defs/HardwareSpec",
      "description": "Boot-time hardware settings. Changing them restarts the device."
    },
    "os": {
      "description": "A Suta OS version such as `2026.09.1`, or `latest` (the default).",
      "type": [
        "string",
        "null"
      ]
    },
    "probation": {
      "anyOf": [
        {
          "$ref": "#/$defs/Duration"
        },
        {
          "type": "null"
        }
      ],
      "description": "How long a candidate must stay healthy: seconds, or `90s`, `2m`, `1h`."
    },
    "project": {
      "description": "The project this file deploys: its slug, such as `drone`. Plans cover\nonly that project's devices.",
      "pattern": "^[a-z0-9]([a-z0-9-]{0,38}[a-z0-9])?$",
      "type": "string"
    },
    "registries": {
      "additionalProperties": {
        "$ref": "#/$defs/RegistrySpec"
      },
      "description": "Credentials for private registries: host → username and secret name.",
      "type": "object"
    },
    "sites": {
      "additionalProperties": {
        "$ref": "#/$defs/Override"
      },
      "description": "Overrides for devices in a site.",
      "type": "object"
    }
  },
  "required": [
    "project"
  ],
  "title": "suta.yaml",
  "type": "object"
}
