{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://0x1audio.com/schema/app/v1.json",
  "title": "Mixel app (app.json v1)",
  "description": "What Mixel reads from an app. mixel check, the desktop and the registry apply the same rules. Generated docs and validators come from this file (sdk-py/contract).",
  "type": "object",
  "additionalProperties": false,
  "required": [
    "$schema",
    "name",
    "displayName",
    "version",
    "sdk"
  ],
  "anyOf": [
    {
      "required": [
        "entry"
      ]
    },
    {
      "required": [
        "page"
      ]
    }
  ],
  "properties": {
    "$schema": {
      "const": "https://0x1audio.com/schema/app/v1.json",
      "description": "Always this URL. It says the file is app.json v1."
    },
    "name": {
      "description": "The app's id: lowercase letters, digits and dashes. Never changes.",
      "type": "string",
      "pattern": "^[a-z][a-z0-9]*(-[a-z0-9]+)*$",
      "minLength": 2,
      "maxLength": 48
    },
    "displayName": {
      "description": "The name people see.",
      "type": "string",
      "minLength": 1,
      "maxLength": 48
    },
    "summary": {
      "description": "One sentence about what the app does, for the catalog.",
      "type": "string",
      "minLength": 1,
      "maxLength": 80
    },
    "version": {
      "description": "MAJOR.MINOR.PATCH. Raise it before each release.",
      "type": "string",
      "pattern": "^(0|[1-9][0-9]{0,8})\\.(0|[1-9][0-9]{0,8})\\.(0|[1-9][0-9]{0,8})$"
    },
    "sdk": {
      "description": "The Mixel versions the app runs on, as a caret range like ^0.3.",
      "type": "string",
      "pattern": "^\\^(0|[1-9][0-9]{0,3})\\.(0|[1-9][0-9]{0,3})(\\.(0|[1-9][0-9]{0,3}))?$"
    },
    "entry": {
      "description": "The Python file with the app's @ox.action functions.",
      "type": "string",
      "pattern": "\\.py$",
      "maxLength": 200
    },
    "page": {
      "description": "An optional page, built by Vite.",
      "type": "string",
      "pattern": "\\.html$",
      "maxLength": 200
    },
    "permissions": {
      "description": "What the app may use, each with the reason the person reads. Binding: anything else is refused.",
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "files": {
          "$ref": "#/$defs/reason",
          "description": "Open the files the person chooses, and write next to them."
        },
        "library": {
          "$ref": "#/$defs/reason",
          "description": "Add sounds to the person's Library."
        },
        "data": {
          "$ref": "#/$defs/reason",
          "description": "Keep the app's own files on this computer (ox.data)."
        },
        "gpu": {
          "$ref": "#/$defs/reason",
          "description": "Use this computer's graphics chip (Metal on a Mac, CUDA on Linux) to run models faster. Without it, models run on the CPU."
        },
        "network": {
          "type": "object",
          "additionalProperties": false,
          "required": [
            "why",
            "hosts"
          ],
          "properties": {
            "why": {
              "$ref": "#/$defs/reason"
            },
            "hosts": {
              "description": "The domains the app reaches. The whole web, IPs and localhost are refused.",
              "type": "array",
              "minItems": 1,
              "maxItems": 16,
              "items": {
                "type": "string",
                "pattern": "^(?=.{1,253}$)([a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)(\\.[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)+$"
              }
            }
          },
          "description": "{\"why\": reason, \"hosts\": [domains]}. Only these hosts are reachable."
        }
      }
    },
    "accent": {
      "description": "The app's colour, like #5b8cff.",
      "type": "string",
      "pattern": "^#[0-9a-fA-F]{6}$"
    },
    "icon": {
      "description": "A square .svg or .png inside the app.",
      "type": "string",
      "pattern": "\\.(svg|png|SVG|PNG)$",
      "maxLength": 200
    },
    "window": {
      "type": "object",
      "additionalProperties": false,
      "minProperties": 1,
      "properties": {
        "width": {
          "type": "integer",
          "minimum": 400,
          "maximum": 2560
        },
        "height": {
          "type": "integer",
          "minimum": 320,
          "maximum": 1600
        },
        "minWidth": {
          "type": "integer",
          "minimum": 400,
          "maximum": 2560
        },
        "minHeight": {
          "type": "integer",
          "minimum": 320,
          "maximum": 1600
        }
      },
      "description": "The size the app's window opens at, in pixels."
    },
    "tags": {
      "type": "array",
      "maxItems": 5,
      "items": {
        "type": "string",
        "minLength": 1,
        "maxLength": 64,
        "pattern": "^\\S+$"
      },
      "description": "Up to 5 words for the catalog, like \"convert\"."
    },
    "license": {
      "type": "string",
      "maxLength": 64,
      "pattern": "^[A-Za-z0-9.+-]+(?: (?:AND|OR|WITH) [A-Za-z0-9.+-]+)*$",
      "description": "The app's licence, an SPDX identifier like \"Apache-2.0\". mixel new writes Apache-2.0; change it if you choose another."
    }
  },
  "$defs": {
    "reason": {
      "description": "One sentence that tells the person why.",
      "type": "string",
      "minLength": 12,
      "maxLength": 120
    }
  }
}
