{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "access": {
      "type": "object",
      "properties": {
        "sshTunnel": {
          "type": "object",
          "properties": {
            "hostPort": { "type": "integer", "minimum": 1024, "maximum": 65535 }
          },
          "additionalProperties": false
        }
      },
      "additionalProperties": false
    },
    "autoDeploy": { "type": "boolean" },
    "backup": {
      "type": "object",
      "properties": {
        "integration": {
          "type": "string",
          "minLength": 1,
          "maxLength": 64,
          "pattern": "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$"
        },
        "azureBlob": {
          "type": "object",
          "properties": {
            "container": {
              "type": "string",
              "pattern": "^[a-z0-9][a-z0-9-]{1,61}[a-z0-9]$"
            },
            "prefix": { "type": "string", "maxLength": 512 },
            "storageAccount": {
              "type": "string",
              "pattern": "^[a-z0-9]{3,24}$"
            }
          },
          "additionalProperties": false
        },
        "gcs": {
          "type": "object",
          "properties": {
            "bucket": {
              "type": "string",
              "pattern": "^(?!\\d+\\.\\d+\\.\\d+\\.\\d+$)[a-z0-9][a-z0-9._-]{1,61}[a-z0-9]$"
            },
            "prefix": { "type": "string", "maxLength": 512 },
            "region": { "type": "string", "minLength": 1, "maxLength": 64 }
          },
          "additionalProperties": false
        },
        "restoreFrom": { "type": "string", "enum": ["s3", "gcs", "azureBlob"] },
        "retention": {
          "type": "object",
          "properties": {
            "keepLast": { "type": "integer", "minimum": 1, "maximum": 100 }
          },
          "additionalProperties": false
        },
        "s3": {
          "type": "object",
          "properties": {
            "bucket": {
              "type": "string",
              "pattern": "^(?!\\d+\\.\\d+\\.\\d+\\.\\d+$)[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$"
            },
            "encryption": { "type": "string", "enum": ["AES256", "aws:kms"] },
            "kmsKeyId": { "type": "string", "minLength": 1, "maxLength": 2048 },
            "prefix": { "type": "string", "maxLength": 512 },
            "region": { "type": "string", "minLength": 1, "maxLength": 64 }
          },
          "additionalProperties": false
        },
        "schedule": {
          "type": "object",
          "properties": {
            "cron": { "type": "string", "minLength": 1, "maxLength": 120 },
            "timezone": { "type": "string", "const": "UTC" }
          },
          "additionalProperties": false
        }
      },
      "additionalProperties": false
    },
    "logDrains": {
      "maxItems": 6,
      "type": "array",
      "items": {
        "type": "string",
        "enum": ["newrelic", "axiom", "betterstack", "datadog", "otlp", "loki"]
      }
    },
    "externalSecrets": {
      "type": "object",
      "propertyNames": { "type": "string", "minLength": 1, "maxLength": 256 },
      "additionalProperties": {
        "type": "object",
        "properties": {
          "integration": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64,
            "pattern": "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$"
          },
          "secret": { "type": "string", "minLength": 1, "maxLength": 1024 },
          "field": { "type": "string", "minLength": 1, "maxLength": 256 },
          "version": { "type": "string", "minLength": 1, "maxLength": 256 },
          "use": { "type": "string", "enum": ["runtime", "build"] }
        },
        "required": ["integration", "secret", "use"],
        "additionalProperties": false
      }
    },
    "telemetry": {
      "type": "object",
      "properties": {
        "integration": {
          "type": "string",
          "minLength": 1,
          "maxLength": 64,
          "pattern": "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$"
        },
        "signals": {
          "minItems": 1,
          "maxItems": 3,
          "type": "array",
          "items": { "type": "string", "enum": ["logs", "metrics", "traces"] }
        },
        "protocol": {
          "default": "otlp-grpc",
          "type": "string",
          "enum": ["otlp-http", "otlp-grpc"]
        },
        "sampling": {
          "default": 1,
          "type": "number",
          "minimum": 0,
          "maximum": 1
        },
        "redactAttributes": {
          "maxItems": 100,
          "type": "array",
          "items": { "type": "string", "minLength": 1, "maxLength": 256 }
        },
        "cardinalityLimit": {
          "default": 10000,
          "type": "integer",
          "minimum": 100,
          "maximum": 1000000
        }
      },
      "additionalProperties": false
    },
    "ingress": {
      "oneOf": [
        {
          "type": "object",
          "properties": { "type": { "type": "string", "const": "proxy" } },
          "required": ["type"],
          "additionalProperties": false
        },
        {
          "type": "object",
          "properties": {
            "type": { "type": "string", "const": "cloudflare-tunnel" },
            "integration": {
              "type": "string",
              "minLength": 1,
              "maxLength": 64,
              "pattern": "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$"
            },
            "tunnel": { "type": "string", "minLength": 1, "maxLength": 128 },
            "access": { "default": false, "type": "boolean" }
          },
          "required": ["type", "integration"],
          "additionalProperties": false
        }
      ]
    },
    "id": {
      "type": "string",
      "pattern": "^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$"
    },
    "name": { "type": "string", "minLength": 1, "maxLength": 120 },
    "description": { "type": "string", "maxLength": 500 },
    "type": {
      "type": "string",
      "enum": [
        "image",
        "postgres",
        "mysql",
        "mariadb",
        "mongodb",
        "redis",
        "dragonfly",
        "keydb",
        "clickhouse"
      ]
    },
    "image": {
      "type": "string",
      "pattern": "^[A-Za-z0-9][A-Za-z0-9._/:@-]{0,511}$"
    },
    "server": {
      "$schema": "https://json-schema.org/draft/2020-12/schema",
      "type": "string"
    },
    "container": {
      "type": "object",
      "properties": {
        "command": {
          "minItems": 1,
          "maxItems": 64,
          "type": "array",
          "items": { "type": "string", "minLength": 1, "maxLength": 4096 }
        },
        "network": {
          "type": "string",
          "pattern": "^[A-Za-z0-9][A-Za-z0-9_.-]{0,127}$"
        },
        "networkAlias": {
          "type": "string",
          "pattern": "^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$"
        },
        "port": { "type": "integer", "minimum": 1, "maximum": 65535 },
        "resources": {
          "type": "object",
          "properties": {
            "cpus": { "type": "number", "exclusiveMinimum": 0, "maximum": 128 },
            "memory": {
              "type": "string",
              "pattern": "^\\d+(?:\\.\\d+)?[bkmg]$"
            }
          },
          "additionalProperties": false
        },
        "volumes": {
          "maxItems": 20,
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "name": {
                "type": "string",
                "pattern": "^[A-Za-z0-9][A-Za-z0-9_.-]{0,127}$"
              },
              "mountPath": {
                "type": "string",
                "minLength": 1,
                "maxLength": 1024
              }
            },
            "required": ["name", "mountPath"],
            "additionalProperties": false
          }
        }
      },
      "additionalProperties": false
    },
    "health": {
      "type": "object",
      "properties": {
        "type": { "type": "string", "enum": ["command", "container", "http"] },
        "path": { "type": "string", "maxLength": 1024, "pattern": "^\\/.*" },
        "command": {
          "minItems": 1,
          "maxItems": 64,
          "type": "array",
          "items": { "type": "string", "minLength": 1, "maxLength": 4096 }
        },
        "timeoutSeconds": { "type": "integer", "minimum": 5, "maximum": 600 }
      },
      "additionalProperties": false
    },
    "domains": {
      "type": "object",
      "properties": {
        "primary": { "type": "string", "minLength": 1, "maxLength": 253 },
        "redirects": {
          "maxItems": 20,
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "host": { "type": "string", "minLength": 1, "maxLength": 253 },
              "status": {
                "anyOf": [
                  { "type": "number", "const": 301 },
                  { "type": "number", "const": 302 }
                ]
              }
            },
            "required": ["host"],
            "additionalProperties": false
          }
        }
      },
      "additionalProperties": false
    },
    "tls": {
      "type": "object",
      "properties": {
        "mode": { "type": "string", "enum": ["direct", "cloudflare-dns"] }
      },
      "additionalProperties": false
    },
    "secrets": {
      "$schema": "https://json-schema.org/draft/2020-12/schema",
      "type": "object",
      "properties": {
        "runtime": {
          "default": [],
          "maxItems": 200,
          "type": "array",
          "items": {
            "type": "string",
            "minLength": 1,
            "maxLength": 1024,
            "pattern": "^[A-Za-z_][A-Za-z0-9_]*$"
          }
        }
      },
      "additionalProperties": false
    },
    "environments": {
      "type": "object",
      "propertyNames": {
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "type": "string",
        "pattern": "^[a-z][a-z0-9-]{0,62}$",
        "not": {
          "enum": [
            "preview",
            "previews",
            "__proto__",
            "constructor",
            "prototype"
          ]
        }
      },
      "additionalProperties": {
        "type": "object",
        "properties": {
          "access": {
            "type": "object",
            "properties": {
              "sshTunnel": {
                "type": "object",
                "properties": {
                  "hostPort": {
                    "type": "integer",
                    "minimum": 1024,
                    "maximum": 65535
                  }
                },
                "additionalProperties": false
              }
            },
            "additionalProperties": false
          },
          "autoDeploy": { "type": "boolean" },
          "backup": {
            "type": "object",
            "properties": {
              "integration": {
                "type": "string",
                "minLength": 1,
                "maxLength": 64,
                "pattern": "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$"
              },
              "azureBlob": {
                "type": "object",
                "properties": {
                  "container": {
                    "type": "string",
                    "pattern": "^[a-z0-9][a-z0-9-]{1,61}[a-z0-9]$"
                  },
                  "prefix": { "type": "string", "maxLength": 512 },
                  "storageAccount": {
                    "type": "string",
                    "pattern": "^[a-z0-9]{3,24}$"
                  }
                },
                "additionalProperties": false
              },
              "gcs": {
                "type": "object",
                "properties": {
                  "bucket": {
                    "type": "string",
                    "pattern": "^(?!\\d+\\.\\d+\\.\\d+\\.\\d+$)[a-z0-9][a-z0-9._-]{1,61}[a-z0-9]$"
                  },
                  "prefix": { "type": "string", "maxLength": 512 },
                  "region": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 64
                  }
                },
                "additionalProperties": false
              },
              "restoreFrom": {
                "type": "string",
                "enum": ["s3", "gcs", "azureBlob"]
              },
              "retention": {
                "type": "object",
                "properties": {
                  "keepLast": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 100
                  }
                },
                "additionalProperties": false
              },
              "s3": {
                "type": "object",
                "properties": {
                  "bucket": {
                    "type": "string",
                    "pattern": "^(?!\\d+\\.\\d+\\.\\d+\\.\\d+$)[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$"
                  },
                  "encryption": {
                    "type": "string",
                    "enum": ["AES256", "aws:kms"]
                  },
                  "kmsKeyId": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 2048
                  },
                  "prefix": { "type": "string", "maxLength": 512 },
                  "region": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 64
                  }
                },
                "additionalProperties": false
              },
              "schedule": {
                "type": "object",
                "properties": {
                  "cron": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 120
                  },
                  "timezone": { "type": "string", "const": "UTC" }
                },
                "additionalProperties": false
              }
            },
            "additionalProperties": false
          },
          "logDrains": {
            "maxItems": 6,
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "newrelic",
                "axiom",
                "betterstack",
                "datadog",
                "otlp",
                "loki"
              ]
            }
          },
          "externalSecrets": {
            "type": "object",
            "propertyNames": {
              "type": "string",
              "minLength": 1,
              "maxLength": 256
            },
            "additionalProperties": {
              "type": "object",
              "properties": {
                "integration": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 64,
                  "pattern": "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$"
                },
                "secret": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 1024
                },
                "field": { "type": "string", "minLength": 1, "maxLength": 256 },
                "version": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 256
                },
                "use": { "type": "string", "enum": ["runtime", "build"] }
              },
              "required": ["integration", "secret", "use"],
              "additionalProperties": false
            }
          },
          "telemetry": {
            "type": "object",
            "properties": {
              "integration": {
                "type": "string",
                "minLength": 1,
                "maxLength": 64,
                "pattern": "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$"
              },
              "signals": {
                "minItems": 1,
                "maxItems": 3,
                "type": "array",
                "items": {
                  "type": "string",
                  "enum": ["logs", "metrics", "traces"]
                }
              },
              "protocol": {
                "default": "otlp-grpc",
                "type": "string",
                "enum": ["otlp-http", "otlp-grpc"]
              },
              "sampling": {
                "default": 1,
                "type": "number",
                "minimum": 0,
                "maximum": 1
              },
              "redactAttributes": {
                "maxItems": 100,
                "type": "array",
                "items": { "type": "string", "minLength": 1, "maxLength": 256 }
              },
              "cardinalityLimit": {
                "default": 10000,
                "type": "integer",
                "minimum": 100,
                "maximum": 1000000
              }
            },
            "additionalProperties": false
          },
          "ingress": {
            "oneOf": [
              {
                "type": "object",
                "properties": {
                  "type": { "type": "string", "const": "proxy" }
                },
                "required": ["type"],
                "additionalProperties": false
              },
              {
                "type": "object",
                "properties": {
                  "type": { "type": "string", "const": "cloudflare-tunnel" },
                  "integration": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 64,
                    "pattern": "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$"
                  },
                  "tunnel": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 128
                  },
                  "access": { "default": false, "type": "boolean" }
                },
                "required": ["type", "integration"],
                "additionalProperties": false
              }
            ]
          },
          "description": { "type": "string", "maxLength": 500 },
          "image": {
            "type": "string",
            "pattern": "^[A-Za-z0-9][A-Za-z0-9._/:@-]{0,511}$"
          },
          "server": {
            "$schema": "https://json-schema.org/draft/2020-12/schema",
            "type": "string"
          },
          "container": {
            "type": "object",
            "properties": {
              "command": {
                "minItems": 1,
                "maxItems": 64,
                "type": "array",
                "items": { "type": "string", "minLength": 1, "maxLength": 4096 }
              },
              "network": {
                "type": "string",
                "pattern": "^[A-Za-z0-9][A-Za-z0-9_.-]{0,127}$"
              },
              "networkAlias": {
                "type": "string",
                "pattern": "^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$"
              },
              "port": { "type": "integer", "minimum": 1, "maximum": 65535 },
              "resources": {
                "type": "object",
                "properties": {
                  "cpus": {
                    "type": "number",
                    "exclusiveMinimum": 0,
                    "maximum": 128
                  },
                  "memory": {
                    "type": "string",
                    "pattern": "^\\d+(?:\\.\\d+)?[bkmg]$"
                  }
                },
                "additionalProperties": false
              },
              "volumes": {
                "maxItems": 20,
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "name": {
                      "type": "string",
                      "pattern": "^[A-Za-z0-9][A-Za-z0-9_.-]{0,127}$"
                    },
                    "mountPath": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 1024
                    }
                  },
                  "required": ["name", "mountPath"],
                  "additionalProperties": false
                }
              }
            },
            "additionalProperties": false
          },
          "health": {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "enum": ["command", "container", "http"]
              },
              "path": {
                "type": "string",
                "maxLength": 1024,
                "pattern": "^\\/.*"
              },
              "command": {
                "minItems": 1,
                "maxItems": 64,
                "type": "array",
                "items": { "type": "string", "minLength": 1, "maxLength": 4096 }
              },
              "timeoutSeconds": {
                "type": "integer",
                "minimum": 5,
                "maximum": 600
              }
            },
            "additionalProperties": false
          },
          "domains": {
            "type": "object",
            "properties": {
              "primary": { "type": "string", "minLength": 1, "maxLength": 253 },
              "redirects": {
                "maxItems": 20,
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "host": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 253
                    },
                    "status": {
                      "anyOf": [
                        { "type": "number", "const": 301 },
                        { "type": "number", "const": 302 }
                      ]
                    }
                  },
                  "required": ["host"],
                  "additionalProperties": false
                }
              }
            },
            "additionalProperties": false
          },
          "tls": {
            "type": "object",
            "properties": {
              "mode": { "type": "string", "enum": ["direct", "cloudflare-dns"] }
            },
            "additionalProperties": false
          }
        },
        "additionalProperties": false
      }
    }
  },
  "additionalProperties": false,
  "required": ["id", "name", "type", "environments"],
  "description": "Entity defaults are merged with the selected environment before full validation during sync. Branch mappings and secret values are managed in Towbar.",
  "$id": "https://www.towbar.dev/schemas/resource.v2.json"
}
