{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://projectcontour.io/schemas/httpproxy.json",
  "title": "Contour HTTPProxy",
  "description": "Schema for the Contour HTTPProxy custom resource definition. HTTPProxy extends Kubernetes ingress with multi-team support, TLS delegation, weighted load balancing, and cross-namespace route inclusion.",
  "type": "object",
  "required": ["apiVersion", "kind", "metadata", "spec"],
  "properties": {
    "apiVersion": {
      "type": "string",
      "description": "API version of the HTTPProxy resource.",
      "const": "projectcontour.io/v1"
    },
    "kind": {
      "type": "string",
      "description": "Resource kind.",
      "const": "HTTPProxy"
    },
    "metadata": {
      "$ref": "#/$defs/ObjectMeta"
    },
    "spec": {
      "$ref": "#/$defs/HTTPProxySpec"
    },
    "status": {
      "$ref": "#/$defs/HTTPProxyStatus"
    }
  },
  "$defs": {
    "HTTPProxySpec": {
      "type": "object",
      "description": "Specification of the HTTPProxy defining routing behavior and virtual host configuration.",
      "properties": {
        "virtualhost": {
          "$ref": "#/$defs/VirtualHost"
        },
        "includes": {
          "type": "array",
          "description": "List of child HTTPProxy resources to include as sub-routes. Enables multi-team delegation of routing configuration.",
          "items": {
            "$ref": "#/$defs/Include"
          }
        },
        "routes": {
          "type": "array",
          "description": "List of routing rules mapping request conditions to backend services.",
          "items": {
            "$ref": "#/$defs/Route"
          }
        },
        "tcpproxy": {
          "$ref": "#/$defs/TCPProxy"
        },
        "ingressClassName": {
          "type": "string",
          "description": "Name of the IngressClass resource to use for this HTTPProxy."
        }
      }
    },
    "VirtualHost": {
      "type": "object",
      "description": "Defines the virtual host for a root HTTPProxy, including its FQDN and TLS settings.",
      "required": ["fqdn"],
      "properties": {
        "fqdn": {
          "type": "string",
          "description": "Fully qualified domain name of the virtual host.",
          "pattern": "^[a-zA-Z0-9*][a-zA-Z0-9.-]*$",
          "examples": ["app.example.com", "*.example.com"]
        },
        "tls": {
          "$ref": "#/$defs/TLS"
        },
        "rateLimitPolicy": {
          "$ref": "#/$defs/RateLimitPolicy"
        },
        "corsPolicy": {
          "$ref": "#/$defs/CORSPolicy"
        },
        "authorization": {
          "$ref": "#/$defs/AuthorizationServer"
        }
      }
    },
    "TLS": {
      "type": "object",
      "description": "TLS configuration for a virtual host including certificate reference and protocol settings.",
      "properties": {
        "secretName": {
          "type": "string",
          "description": "Name of the Kubernetes Secret containing the TLS certificate and key. May reference a Secret in another namespace using 'namespace/name' format if TLSCertificateDelegation grants access."
        },
        "minimumProtocolVersion": {
          "type": "string",
          "description": "Minimum TLS protocol version the virtual host will negotiate.",
          "enum": ["1.2", "1.3"]
        },
        "passthrough": {
          "type": "boolean",
          "description": "If true, TLS termination is performed by the backend service rather than by Envoy."
        },
        "enableFallbackCertificate": {
          "type": "boolean",
          "description": "If true, Contour will serve a fallback certificate for SNI names that don't match any configured virtual host."
        },
        "clientValidation": {
          "type": "object",
          "description": "Configuration for mTLS client certificate validation.",
          "properties": {
            "caSecret": {
              "type": "string",
              "description": "Name of the Secret containing the CA certificate for client validation."
            },
            "skipClientCertValidation": {
              "type": "boolean",
              "description": "If true, client certificates are requested but not required to be valid."
            }
          }
        }
      }
    },
    "Include": {
      "type": "object",
      "description": "A reference to another HTTPProxy to include as child routes, enabling cross-namespace routing delegation.",
      "required": ["name"],
      "properties": {
        "name": {
          "type": "string",
          "description": "Name of the HTTPProxy resource to include."
        },
        "namespace": {
          "type": "string",
          "description": "Namespace of the included HTTPProxy. If omitted, defaults to the current namespace."
        },
        "conditions": {
          "type": "array",
          "description": "Conditions that requests must match for this inclusion to apply.",
          "items": {
            "$ref": "#/$defs/MatchCondition"
          }
        }
      }
    },
    "Route": {
      "type": "object",
      "description": "A routing rule that maps matched HTTP requests to backend services.",
      "properties": {
        "conditions": {
          "type": "array",
          "description": "Match conditions for this route. All conditions must match for the route to apply.",
          "items": {
            "$ref": "#/$defs/MatchCondition"
          }
        },
        "services": {
          "type": "array",
          "description": "Backend services to route matching traffic to. Multiple services enable weighted load balancing.",
          "items": {
            "$ref": "#/$defs/Service"
          }
        },
        "enableWebsockets": {
          "type": "boolean",
          "description": "If true, Contour will upgrade HTTP connections to WebSocket connections for this route."
        },
        "permitInsecure": {
          "type": "boolean",
          "description": "If true, allows the route to serve non-TLS requests when the parent virtual host has TLS configured."
        },
        "retryPolicy": {
          "$ref": "#/$defs/RetryPolicy"
        },
        "timeoutPolicy": {
          "$ref": "#/$defs/TimeoutPolicy"
        },
        "rateLimitPolicy": {
          "$ref": "#/$defs/RateLimitPolicy"
        },
        "requestHeadersPolicy": {
          "$ref": "#/$defs/HeadersPolicy"
        },
        "responseHeadersPolicy": {
          "$ref": "#/$defs/HeadersPolicy"
        },
        "pathRewritePolicy": {
          "type": "object",
          "description": "Policy for rewriting the URL path before forwarding to backends.",
          "properties": {
            "replacePrefix": {
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "prefix": {
                    "type": "string",
                    "description": "The path prefix to replace."
                  },
                  "replacement": {
                    "type": "string",
                    "description": "The replacement prefix."
                  }
                }
              }
            }
          }
        }
      }
    },
    "MatchCondition": {
      "type": "object",
      "description": "A condition that must be satisfied for a route or include to apply.",
      "properties": {
        "prefix": {
          "type": "string",
          "description": "URL path prefix that incoming requests must start with.",
          "examples": ["/api", "/static"]
        },
        "exact": {
          "type": "string",
          "description": "Exact URL path that incoming requests must match."
        },
        "regex": {
          "type": "string",
          "description": "Regular expression that the URL path must match."
        },
        "header": {
          "$ref": "#/$defs/HeaderMatchCondition"
        },
        "queryParameter": {
          "$ref": "#/$defs/QueryParameterMatchCondition"
        }
      }
    },
    "HeaderMatchCondition": {
      "type": "object",
      "description": "A condition matching on an HTTP request header value.",
      "required": ["name"],
      "properties": {
        "name": {
          "type": "string",
          "description": "Name of the HTTP header to match."
        },
        "present": {
          "type": "boolean",
          "description": "If true, matches requests where the header is present."
        },
        "notpresent": {
          "type": "boolean",
          "description": "If true, matches requests where the header is absent."
        },
        "contains": {
          "type": "string",
          "description": "Matches if the header value contains this substring."
        },
        "notcontains": {
          "type": "string",
          "description": "Matches if the header value does not contain this substring."
        },
        "exact": {
          "type": "string",
          "description": "Matches if the header value equals this string exactly."
        },
        "notexact": {
          "type": "string",
          "description": "Matches if the header value does not equal this string."
        },
        "regex": {
          "type": "string",
          "description": "Matches if the header value matches this regular expression."
        }
      }
    },
    "QueryParameterMatchCondition": {
      "type": "object",
      "description": "A condition matching on an HTTP request query parameter.",
      "required": ["name"],
      "properties": {
        "name": {
          "type": "string",
          "description": "Name of the query parameter to match."
        },
        "exact": {
          "type": "string",
          "description": "Matches if the query parameter equals this value."
        },
        "prefix": {
          "type": "string",
          "description": "Matches if the query parameter starts with this value."
        },
        "suffix": {
          "type": "string",
          "description": "Matches if the query parameter ends with this value."
        },
        "contains": {
          "type": "string",
          "description": "Matches if the query parameter contains this substring."
        },
        "present": {
          "type": "boolean",
          "description": "If true, matches requests where the query parameter is present."
        },
        "regex": {
          "type": "string",
          "description": "Matches if the query parameter value matches this regular expression."
        }
      }
    },
    "Service": {
      "type": "object",
      "description": "A backend Kubernetes Service to route traffic to.",
      "required": ["name", "port"],
      "properties": {
        "name": {
          "type": "string",
          "description": "Name of the Kubernetes Service in the same namespace."
        },
        "port": {
          "type": "integer",
          "description": "Port number on the Kubernetes Service.",
          "minimum": 1,
          "maximum": 65535
        },
        "weight": {
          "type": "integer",
          "description": "Relative traffic weight. When multiple services are specified, traffic is distributed proportionally by weight.",
          "minimum": 0
        },
        "protocol": {
          "type": "string",
          "description": "Protocol to use when connecting to the upstream service.",
          "enum": ["h2", "h2c", "tls"]
        },
        "healthPort": {
          "type": "integer",
          "description": "Port to use for health checking if different from the service port.",
          "minimum": 1,
          "maximum": 65535
        }
      }
    },
    "TCPProxy": {
      "type": "object",
      "description": "TCP proxy configuration for TLS passthrough or raw TCP routing.",
      "properties": {
        "services": {
          "type": "array",
          "description": "Backend services for TCP proxying.",
          "items": {
            "$ref": "#/$defs/Service"
          }
        },
        "include": {
          "$ref": "#/$defs/Include"
        }
      }
    },
    "RetryPolicy": {
      "type": "object",
      "description": "Configuration for retrying failed upstream requests.",
      "properties": {
        "count": {
          "type": "integer",
          "description": "Number of retry attempts before returning an error to the client.",
          "minimum": 0
        },
        "perTryTimeout": {
          "type": "string",
          "description": "Timeout per retry attempt as a duration string (e.g. '10s', '1m')."
        },
        "retriableStatusCodes": {
          "type": "array",
          "description": "HTTP status codes from the backend that trigger a retry.",
          "items": {
            "type": "integer",
            "minimum": 400,
            "maximum": 599
          }
        }
      }
    },
    "TimeoutPolicy": {
      "type": "object",
      "description": "Timeout configuration for requests on this route.",
      "properties": {
        "response": {
          "type": "string",
          "description": "Timeout for the entire response from the backend, as a duration string.",
          "examples": ["15s", "1m", "infinity"]
        },
        "idle": {
          "type": "string",
          "description": "Timeout for idle connections on this route."
        },
        "idleConnection": {
          "type": "string",
          "description": "Timeout for idle backend connections in the connection pool."
        }
      }
    },
    "RateLimitPolicy": {
      "type": "object",
      "description": "Rate limiting configuration applied to this route or virtual host.",
      "properties": {
        "local": {
          "type": "object",
          "description": "Local rate limiting using Envoy's built-in token bucket algorithm, enforced per Envoy pod.",
          "properties": {
            "requests": {
              "type": "integer",
              "description": "Maximum number of requests allowed per time unit.",
              "minimum": 1
            },
            "unit": {
              "type": "string",
              "description": "Time unit for measuring request rate.",
              "enum": ["second", "minute", "hour"]
            },
            "burst": {
              "type": "integer",
              "description": "Maximum burst above the rate limit that is momentarily allowed.",
              "minimum": 0
            },
            "responseHeadersToAdd": {
              "type": "array",
              "description": "Headers to add to rate-limited responses.",
              "items": {
                "$ref": "#/$defs/HeaderValue"
              }
            }
          },
          "required": ["requests", "unit"]
        },
        "global": {
          "type": "object",
          "description": "Global rate limiting via an external rate limit service shared across all Envoy pods.",
          "properties": {
            "descriptors": {
              "type": "array",
              "description": "Rate limit descriptors sent to the global rate limit service.",
              "items": {
                "type": "object"
              }
            }
          }
        }
      }
    },
    "CORSPolicy": {
      "type": "object",
      "description": "Cross-Origin Resource Sharing (CORS) policy for the virtual host.",
      "required": ["allowOrigin", "allowMethods"],
      "properties": {
        "allowCredentials": {
          "type": "boolean",
          "description": "Whether to include credentials in CORS responses."
        },
        "allowOrigin": {
          "type": "array",
          "description": "List of origins allowed to make cross-site requests. Use '*' to allow all origins.",
          "items": {
            "type": "string"
          }
        },
        "allowMethods": {
          "type": "array",
          "description": "HTTP methods allowed in cross-origin requests.",
          "items": {
            "type": "string",
            "enum": ["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"]
          }
        },
        "allowHeaders": {
          "type": "array",
          "description": "Request headers that are allowed in cross-origin requests.",
          "items": {
            "type": "string"
          }
        },
        "exposeHeaders": {
          "type": "array",
          "description": "Response headers that the browser is allowed to access.",
          "items": {
            "type": "string"
          }
        },
        "maxAge": {
          "type": "string",
          "description": "Duration to cache CORS preflight responses, as a duration string.",
          "examples": ["600s", "1h"]
        }
      }
    },
    "AuthorizationServer": {
      "type": "object",
      "description": "External authorization server configuration for validating requests to the virtual host.",
      "properties": {
        "extensionRef": {
          "type": "object",
          "description": "Reference to an ExtensionService providing the external authorization server.",
          "required": ["name"],
          "properties": {
            "name": {
              "type": "string",
              "description": "Name of the ExtensionService resource."
            },
            "namespace": {
              "type": "string",
              "description": "Namespace of the ExtensionService resource."
            }
          }
        },
        "authPolicy": {
          "type": "object",
          "description": "Policy settings for the authorization check.",
          "properties": {
            "disabled": {
              "type": "boolean",
              "description": "If true, disables authorization for this virtual host or route."
            },
            "context": {
              "type": "object",
              "description": "Additional context key-value pairs to include in authorization requests.",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "failOpen": {
          "type": "boolean",
          "description": "If true, requests proceed even if the authorization service is unavailable."
        }
      }
    },
    "HeadersPolicy": {
      "type": "object",
      "description": "Policy for adding, setting, or removing HTTP headers on requests or responses.",
      "properties": {
        "set": {
          "type": "array",
          "description": "Headers to set, overriding any existing value.",
          "items": {
            "$ref": "#/$defs/HeaderValue"
          }
        },
        "remove": {
          "type": "array",
          "description": "Header names to remove before forwarding.",
          "items": {
            "type": "string"
          }
        }
      }
    },
    "HeaderValue": {
      "type": "object",
      "description": "A header name-value pair.",
      "required": ["name", "value"],
      "properties": {
        "name": {
          "type": "string",
          "description": "HTTP header name."
        },
        "value": {
          "type": "string",
          "description": "HTTP header value. May reference request metadata using dynamic values."
        }
      }
    },
    "HTTPProxyStatus": {
      "type": "object",
      "description": "Observed status of an HTTPProxy as reported by the Contour controller.",
      "properties": {
        "currentStatus": {
          "type": "string",
          "description": "High-level processing status of the HTTPProxy.",
          "enum": ["valid", "invalid", "orphaned"]
        },
        "description": {
          "type": "string",
          "description": "Human-readable explanation of the current status."
        },
        "conditions": {
          "type": "array",
          "description": "Detailed conditions describing the state of the HTTPProxy.",
          "items": {
            "$ref": "#/$defs/DetailedCondition"
          }
        }
      }
    },
    "DetailedCondition": {
      "type": "object",
      "description": "A detailed condition providing structured status information.",
      "required": ["type", "status"],
      "properties": {
        "type": {
          "type": "string",
          "description": "Type of condition, e.g. 'Valid'."
        },
        "status": {
          "type": "string",
          "description": "Status of the condition.",
          "enum": ["True", "False", "Unknown"]
        },
        "observedGeneration": {
          "type": "integer",
          "format": "int64",
          "description": "Resource generation when this condition was last evaluated."
        },
        "lastTransitionTime": {
          "type": "string",
          "format": "date-time",
          "description": "Time when this condition last transitioned."
        },
        "reason": {
          "type": "string",
          "description": "Short, machine-readable reason for the condition."
        },
        "message": {
          "type": "string",
          "description": "Human-readable details about the condition."
        },
        "errors": {
          "type": "array",
          "description": "Sub-conditions representing specific errors.",
          "items": {
            "$ref": "#/$defs/SubCondition"
          }
        },
        "warnings": {
          "type": "array",
          "description": "Sub-conditions representing warnings.",
          "items": {
            "$ref": "#/$defs/SubCondition"
          }
        }
      }
    },
    "SubCondition": {
      "type": "object",
      "description": "A sub-condition providing granular detail about an error or warning.",
      "required": ["type", "status"],
      "properties": {
        "type": {
          "type": "string",
          "description": "Type of sub-condition."
        },
        "status": {
          "type": "string",
          "description": "Status of the sub-condition.",
          "enum": ["True", "False", "Unknown"]
        },
        "reason": {
          "type": "string",
          "description": "Short, machine-readable reason."
        },
        "message": {
          "type": "string",
          "description": "Human-readable details."
        }
      }
    },
    "ObjectMeta": {
      "type": "object",
      "description": "Standard Kubernetes object metadata.",
      "required": ["name"],
      "properties": {
        "name": {
          "type": "string",
          "description": "Name of the resource, unique within its namespace.",
          "maxLength": 253,
          "pattern": "^[a-z0-9][a-z0-9.-]*[a-z0-9]$"
        },
        "namespace": {
          "type": "string",
          "description": "Namespace the resource belongs to."
        },
        "labels": {
          "type": "object",
          "description": "Labels for organizing and selecting the resource.",
          "additionalProperties": {
            "type": "string"
          }
        },
        "annotations": {
          "type": "object",
          "description": "Non-identifying metadata for the resource.",
          "additionalProperties": {
            "type": "string"
          }
        }
      }
    }
  },
  "examples": [
    {
      "apiVersion": "projectcontour.io/v1",
      "kind": "HTTPProxy",
      "metadata": {
        "name": "example-proxy",
        "namespace": "default"
      },
      "spec": {
        "virtualhost": {
          "fqdn": "app.example.com",
          "tls": {
            "secretName": "example-tls",
            "minimumProtocolVersion": "1.2"
          }
        },
        "routes": [
          {
            "conditions": [
              {
                "prefix": "/api"
              }
            ],
            "services": [
              {
                "name": "api-service",
                "port": 8080,
                "weight": 100
              }
            ],
            "timeoutPolicy": {
              "response": "15s"
            }
          }
        ]
      }
    }
  ]
}
