{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "https://tikoci.github.io/restraml/routeros-app-yaml-schema.latest.json",
  "title": "RouterOS /app YAML",
  "description": "Schema for MikroTik RouterOS /app YAML",
  "type": "object",
  "required": [
    "services"
  ],
  "additionalProperties": false,
  "properties": {
    "name": {
      "type": "string",
      "description": "Unique name for the /app"
    },
    "descr": {
      "type": "string",
      "description": "Description of the /app"
    },
    "page": {
      "type": "string",
      "format": "uri",
      "description": "URL to the project homepage"
    },
    "category": {
      "type": "string",
      "enum": [
        "productivity",
        "storage",
        "networking",
        "development",
        "communication",
        "file-management",
        "search",
        "video",
        "media",
        "media-management",
        "home-automation",
        "monitoring",
        "database",
        "automation",
        "ai",
        "messaging",
        "radio",
        "security",
        "business"
      ],
      "description": "Category classification for the /app"
    },
    "icon": {
      "type": "string",
      "format": "uri",
      "description": "URL to the icon image (use with `show-in-webfig=yes` to display on WebFig login screen)"
    },
    "default-credentials": {
      "type": [
        "string",
        "null"
      ],
      "description": "Default login credentials in format username:password or special values"
    },
    "url-path": {
      "type": "string",
      "description": "Default URL path to append to access the /app via a browser"
    },
    "credentials": {
      "type": "string",
      "description": "Credential hint displayed in the app UI (alternative to default-credentials)"
    },
    "option": {
      "type": "boolean",
      "description": "Whether this /app is optional"
    },
    "auto-update": {
      "type": "boolean",
      "description": "Whether to enable automatic updates of all containers at boot"
    },
    "secrets": {
      "type": "object",
      "description": "Generated secret declarations referenced by service `secrets` and `[secret:name]` placeholders",
      "patternProperties": {
        "^[a-zA-Z0-9_-]+$": {
          "type": [
            "object",
            "null"
          ],
          "additionalProperties": false
        }
      },
      "additionalProperties": false
    },
    "services": {
      "type": "object",
      "description": "Container services definition",
      "patternProperties": {
        "^[a-zA-Z0-9_-]+$": {
          "type": "object",
          "additionalProperties": false,
          "required": [
            "image"
          ],
          "properties": {
            "image": {
              "type": "string",
              "description": "Container image to use.  Can prepend the container registry, or the `registry-url` in `/container/config` will be assumed."
            },
            "container_name": {
              "type": "string",
              "description": "Explicit container name.  Also used in file paths."
            },
            "hostname": {
              "type": "string",
              "description": "Container hostname"
            },
            "entrypoint": {
              "oneOf": [
                {
                  "type": "string"
                },
                {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                }
              ],
              "description": "Override the default container entrypoint"
            },
            "command": {
              "oneOf": [
                {
                  "type": "string"
                },
                {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                }
              ],
              "description": "Override the default command"
            },
            "ports": {
              "type": "array",
              "description": "Port mappings",
              "items": {
                "$comment": "Port mappings can be strings (two formats) or objects (long syntax with name/target/published/app_protocol).",
                "oneOf": [
                  {
                    "type": "string",
                    "$comment": "Two port formats supported, exactly one protocol style per entry: (1) old OCI-style [ip:]host:container[/tcp|/udp][:label] and (2) new RouterOS 7.23+ style [ip:]host:container[:label][:tcp|:udp]. Mixing styles (e.g. host:container/tcp:label:tcp) is rejected.",
                    "anyOf": [
                      {
                        "$comment": "Old OCI-style: [ip:]host:container[/tcp|/udp][:label]",
                        "pattern": "^(\\d+\\.\\d+\\.\\d+\\.\\d+:|\\[[a-zA-Z0-9]+\\]:)?(\\d+|\\[[a-zA-Z0-9]+\\]):(\\d+|\\[[a-zA-Z0-9]+\\])(\\/tcp|\\/udp)?(:[^:\\n\\r]+)?$"
                      },
                      {
                        "$comment": "New RouterOS 7.23+ style: [ip:]host:container[:label][:tcp|:udp]. Label may not contain / or : to stay distinct from old style.",
                        "pattern": "^(\\d+\\.\\d+\\.\\d+\\.\\d+:|\\[[a-zA-Z0-9]+\\]:)?(\\d+|\\[[a-zA-Z0-9]+\\]):(\\d+|\\[[a-zA-Z0-9]+\\])(:[^:\\/\\n\\r]+)?(:(tcp|udp))?$"
                      }
                    ]
                  },
                  {
                    "type": "object",
                    "$comment": "Long syntax port mapping with named fields",
                    "required": [
                      "target",
                      "published"
                    ],
                    "properties": {
                      "name": {
                        "type": "string",
                        "description": "Port mapping label"
                      },
                      "target": {
                        "type": "integer",
                        "description": "Container port"
                      },
                      "published": {
                        "type": "integer",
                        "description": "Host port"
                      },
                      "protocol": {
                        "type": "string",
                        "enum": [
                          "tcp",
                          "udp"
                        ],
                        "description": "Protocol (tcp or udp)"
                      },
                      "app_protocol": {
                        "type": "string",
                        "description": "Application protocol hint"
                      }
                    },
                    "additionalProperties": false
                  }
                ]
              }
            },
            "environment": {
              "oneOf": [
                {
                  "type": "object",
                  "patternProperties": {
                    "^[A-Z_][A-Z0-9_]*$": {
                      "oneOf": [
                        {
                          "type": "string"
                        },
                        {
                          "type": "number"
                        },
                        {
                          "type": "boolean"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    }
                  }
                },
                {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                },
                {
                  "type": "null"
                }
              ],
              "description": "Environment variables"
            },
            "volumes": {
              "type": "array",
              "description": "Volume mappings",
              "items": {
                "type": "string"
              }
            },
            "configs": {
              "type": "array",
              "description": "Config file mappings",
              "items": {
                "type": "object",
                "required": [
                  "source",
                  "target"
                ],
                "additionalProperties": false,
                "properties": {
                  "source": {
                    "type": "string"
                  },
                  "target": {
                    "type": "string"
                  },
                  "mode": {
                    "type": [
                      "integer",
                      "string"
                    ],
                    "description": "File permissions in octal"
                  }
                }
              }
            },
            "restart": {
              "type": "string",
              "enum": [
                "no",
                "always",
                "on-failure",
                "unless-stopped"
              ],
              "description": "Restart policy"
            },
            "depends_on": {
              "oneOf": [
                {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                },
                {
                  "type": "object"
                }
              ],
              "description": "Service dependencies"
            },
            "devices": {
              "type": "array",
              "description": "Device mappings to pass to the container",
              "items": {
                "type": "string"
              }
            },
            "user": {
              "type": "string",
              "description": "User to run container as"
            },
            "security_opt": {
              "type": "array",
              "items": {
                "type": "string"
              },
              "description": "Security options"
            },
            "shm_size": {
              "type": "string",
              "description": "Shared memory size"
            },
            "stop_grace_period": {
              "type": [
                "string",
                "integer"
              ],
              "description": "Time to wait before killing container"
            },
            "ulimits": {
              "type": "object",
              "description": "Resource limits (e.g. nofile soft/hard)",
              "patternProperties": {
                "^[a-zA-Z0-9_-]+$": {
                  "oneOf": [
                    {
                      "type": "object",
                      "required": ["soft", "hard"],
                      "additionalProperties": false,
                      "properties": {
                        "soft": {
                          "type": "integer"
                        },
                        "hard": {
                          "type": "integer"
                        }
                      }
                    },
                    {
                      "type": "integer"
                    }
                  ]
                }
              }
            },
            "build": {
              "oneOf": [
                {
                  "type": "object",
                  "additionalProperties": false,
                  "properties": {
                    "context": {
                      "type": "string"
                    },
                    "dockerfile": {
                      "type": "string"
                    },
                    "args": {
                      "type": "object"
                    }
                  }
                },
                {
                  "type": "string"
                }
              ],
              "description": "Build configuration for the container image"
            },
            "healthcheck": {
              "type": "object",
              "description": "Container health check configuration",
              "additionalProperties": false,
              "properties": {
                "test": {
                  "oneOf": [
                    {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    {
                      "type": "string"
                    }
                  ]
                },
                "interval": {
                  "type": "string"
                },
                "timeout": {
                  "type": "string"
                },
                "retries": {
                  "type": "integer"
                },
                "start_period": {
                  "type": "string"
                }
              }
            },
            "stdin_open": {
              "type": "boolean",
              "description": "Keep stdin open even if not attached"
            },
            "expose": {
              "type": "array",
              "description": "Expose ports without publishing them to the host",
              "items": {
                "type": [
                  "string",
                  "integer"
                ]
              }
            },
            "secrets": {
              "oneOf": [
                {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                },
                {
                  "type": "string"
                }
              ],
              "description": "Secret name or secret names to expose to the service"
            },
            "secret": {
              "oneOf": [
                {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                },
                {
                  "type": "string"
                }
              ],
              "description": "Singular secret alias accepted by RouterOS built-in app YAML"
            },
            "attach": {
              "type": "boolean",
              "description": "Whether to attach to the container's stdio"
            }
          }
        }
      },
      "minProperties": 1
    },
    "volumes": {
      "type": "object",
      "description": "Named volumes definition",
      "patternProperties": {
        "^[a-zA-Z0-9_-]+$": {
          "type": [
            "object",
            "null"
          ]
        }
      }
    },
    "networks": {
      "type": "object",
      "description": "Networks configuration",
      "patternProperties": {
        "^[a-zA-Z0-9_-]+$": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "name": {
              "type": "string"
            },
            "external": {
              "type": "boolean"
            }
          }
        }
      }
    },
    "configs": {
      "type": "object",
      "description": "Config files definition.  Used along with a service's `configs`, which controls placement of the contents.",
      "patternProperties": {
        "^[a-zA-Z0-9_-]+$": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "content": {
              "type": "string",
              "description": "Inline config file content"
            }
          }
        }
      }
    }
  }
}