{
  "openapi": "3.1.0",
  "info": {
    "title": "catastrophic.io",
    "summary": "A chaos engineering scratchpad — public HTTP endpoints that deliberately misbehave.",
    "description": "Catastrophic.io is a collection of public endpoints designed to misbehave\nin specific, documented ways. Use them to test how your HTTP clients\nhandle slow responses, weird status codes, broken bodies, lying headers,\nintermittent failures, and other adversity.\n\nEvery chaos response includes `X-Chaos-*` headers so monitoring clients\ncan distinguish intentional chaos from real failures.\n\nAll endpoints accept GET unless noted. Unknown paths fall through to the\nstatic catalog at the same domain.\n",
    "version": "1.0.0",
    "contact": {
      "url": "https://catastrophic.io"
    },
    "license": {
      "name": "MIT",
      "identifier": "MIT"
    }
  },
  "servers": [
    {
      "url": "https://chaos.catastrophic.io",
      "description": "Production"
    }
  ],
  "security": [],
  "tags": [
    {
      "name": "latency",
      "description": "Delayed and slowly-streaming responses."
    },
    {
      "name": "status",
      "description": "Arbitrary HTTP status codes."
    },
    {
      "name": "payload",
      "description": "Bodies that misbehave or lie about their size or type."
    },
    {
      "name": "redirects",
      "description": "Redirect chains."
    },
    {
      "name": "headers",
      "description": "Header floods and contradictory caching directives."
    },
    {
      "name": "rate-limiting",
      "description": "429 responses with configurable Retry-After."
    },
    {
      "name": "fault-injection",
      "description": "Random failures at a configurable probability."
    },
    {
      "name": "inspection",
      "description": "Request reflection for debugging."
    },
    {
      "name": "auth",
      "description": "WWW-Authenticate challenges and 401 handling."
    },
    {
      "name": "cors",
      "description": "Broken or missing CORS headers."
    },
    {
      "name": "agent",
      "description": "Semantically wrong responses that pass structural validation."
    },
    {
      "name": "discovery",
      "description": "Mutated .well-known discovery documents."
    },
    {
      "name": "pagination",
      "description": "Paginated responses whose metadata lies, whose pages overlap, or whose Link headers point to the wrong place."
    },
    {
      "name": "method",
      "description": "Method-handling chaos — Allow header lies, 405s without Allow, OPTIONS that contradicts reality."
    }
  ],
  "paths": {
    "/slow": {
      "get": {
        "operationId": "slow",
        "externalDocs": {
          "description": "Endpoint reference on catastrophic.io",
          "url": "https://catastrophic.io/chaos/endpoints/latency/slow/"
        },
        "tags": [
          "latency"
        ],
        "summary": "Sleep then return 200",
        "description": "Sleeps for the specified number of milliseconds before responding. The full response arrives at once after the delay elapses.",
        "parameters": [
          {
            "name": "ms",
            "in": "query",
            "description": "Delay in milliseconds.",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "maximum": 15000,
              "default": 1000
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Returned after the configured delay.",
            "headers": {
              "X-Chaos-Delayed-Ms": {
                "description": "Actual delay applied, in milliseconds.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "endpoint": {
                      "type": "string",
                      "const": "/slow"
                    },
                    "delayed_ms": {
                      "type": "integer"
                    },
                    "max_ms": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/status/{code}": {
      "get": {
        "operationId": "statusCode",
        "externalDocs": {
          "description": "Endpoint reference on catastrophic.io",
          "url": "https://catastrophic.io/chaos/endpoints/status-codes/status-code/"
        },
        "tags": [
          "status"
        ],
        "summary": "Return an arbitrary HTTP status code",
        "description": "Returns the requested status code. Codes 204 and 304 return no body per RFC 9110. Code 101 (and other protocol-switching codes) is rejected with 400.",
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "description": "HTTP status code to return. Clamped to 100–599.",
            "schema": {
              "type": "integer",
              "minimum": 100,
              "maximum": 599
            }
          }
        ],
        "responses": {
          "400": {
            "description": "Returned when the requested code cannot legally be returned by the platform (e.g. 101).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "endpoint": {
                      "type": "string"
                    },
                    "error": {
                      "type": "string"
                    },
                    "requested": {
                      "type": "integer"
                    },
                    "reason": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "default": {
            "description": "The requested status code, with a JSON body describing the response (empty for 204/304).",
            "headers": {
              "X-Chaos-Status": {
                "description": "The status code returned.",
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/truncate": {
      "get": {
        "operationId": "truncate",
        "externalDocs": {
          "description": "Endpoint reference on catastrophic.io",
          "url": "https://catastrophic.io/chaos/endpoints/delivery/truncate/"
        },
        "tags": [
          "payload"
        ],
        "summary": "Send Content-Length for full body, cut at N bytes",
        "description": "Sends a `Content-Length` header claiming the full payload size, but the response stream closes early at N bytes. The payload is a deterministic ASCII pattern (`0123456789` repeating) so clients can verify exactly how many bytes they received versus what was promised.\n",
        "parameters": [
          {
            "name": "at",
            "in": "query",
            "description": "Byte position where the stream is truncated.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 1000000,
              "default": 512
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Truncated body with lying Content-Length.",
            "headers": {
              "Content-Length": {
                "description": "Promised body size (larger than what is actually delivered).",
                "schema": {
                  "type": "string"
                }
              },
              "X-Chaos-Truncated-At": {
                "schema": {
                  "type": "string"
                }
              },
              "X-Chaos-Promised-Bytes": {
                "schema": {
                  "type": "string"
                }
              },
              "X-Chaos-Actual-Bytes": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/json": {
      "parameters": [
        {
          "name": "mode",
          "in": "query",
          "description": "Which kind of JSON flaw to send. `all` (default) bundles three flavors of wrong in one payload (the original behavior); the others isolate a single failure mode.",
          "schema": {
            "type": "string",
            "enum": [
              "all",
              "missing-brace",
              "unquoted-key",
              "trailing-comma"
            ],
            "default": "all"
          }
        }
      ],
      "get": {
        "operationId": "jsonChaos",
        "externalDocs": {
          "description": "Endpoint reference on catastrophic.io",
          "url": "https://catastrophic.io/chaos/endpoints/format/json/"
        },
        "tags": [
          "payload"
        ],
        "summary": "Invalid JSON with application/json Content-Type",
        "description": "Returns a syntactically invalid JSON body with `Content-Type: application/json`. Tests client JSON parsing error handling. Use `?mode=` to test a specific failure mode in isolation.",
        "responses": {
          "200": {
            "description": "Invalid JSON body.",
            "headers": {
              "X-Chaos-Payload": {
                "schema": {
                  "type": "string",
                  "const": "json-chaos"
                }
              },
              "X-Chaos-Json-Mode": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "string",
                  "description": "Not actually valid JSON."
                }
              }
            }
          }
        }
      }
    },
    "/wrong-content-type": {
      "parameters": [
        {
          "name": "type",
          "in": "query",
          "description": "Which kind of body/Content-Type mismatch to send. `json-as-html` (default) sends a JSON body labelled `text/html`. `missing` omits the Content-Type header entirely.",
          "schema": {
            "type": "string",
            "enum": [
              "json-as-html",
              "xml-as-json",
              "html-as-json",
              "json-as-png",
              "missing"
            ],
            "default": "json-as-html"
          }
        }
      ],
      "get": {
        "operationId": "wrongContentType",
        "externalDocs": {
          "description": "Endpoint reference on catastrophic.io",
          "url": "https://catastrophic.io/chaos/endpoints/headers-and-caching/wrong-content-type/"
        },
        "tags": [
          "payload"
        ],
        "summary": "Body labelled with a non-matching Content-Type",
        "description": "Returns a body whose declared `Content-Type` does not match its actual format. Tests whether clients parse by declared type or by content sniffing. `X-Chaos-Body-Format` exposes the body's real format so a client can detect the mismatch programmatically.",
        "responses": {
          "200": {
            "description": "Body with a mismatched (or missing) Content-Type header.",
            "headers": {
              "X-Chaos-Payload": {
                "schema": {
                  "type": "string",
                  "const": "wrong-content-type"
                }
              },
              "X-Chaos-Type-Mode": {
                "schema": {
                  "type": "string"
                }
              },
              "X-Chaos-Body-Format": {
                "schema": {
                  "type": "string"
                }
              },
              "X-Chaos-Actual-Format": {
                "schema": {
                  "type": "string",
                  "description": "Deprecated alias for X-Chaos-Body-Format."
                }
              }
            },
            "content": {
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/redirect-chain": {
      "get": {
        "operationId": "redirectChain",
        "externalDocs": {
          "description": "Endpoint reference on catastrophic.io",
          "url": "https://catastrophic.io/chaos/endpoints/redirects/redirect-chain/"
        },
        "tags": [
          "redirects"
        ],
        "summary": "Redirect N times before resolving to 200",
        "description": "Each hop redirects to `/redirect-chain?n=N-1`. When `n=0`, returns 200. Tests redirect-following limits, loop detection, and history tracking.",
        "parameters": [
          {
            "name": "n",
            "in": "query",
            "description": "Number of redirect hops remaining.",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "maximum": 20,
              "default": 3
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Final hop in the chain.",
            "headers": {
              "X-Chaos-Redirect-Chain": {
                "schema": {
                  "type": "string",
                  "const": "resolved"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "endpoint": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    },
                    "hops_completed": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "302": {
            "description": "Intermediate hop. Location points to the next URL in the chain.",
            "headers": {
              "Location": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/header-flood": {
      "get": {
        "operationId": "headerFlood",
        "externalDocs": {
          "description": "Endpoint reference on catastrophic.io",
          "url": "https://catastrophic.io/chaos/endpoints/headers-and-caching/header-flood/"
        },
        "tags": [
          "headers"
        ],
        "summary": "Return N custom response headers",
        "description": "Returns N synthetic `X-Chaos-Header-NNN` headers alongside the normal response. Tests header size limits and parser behavior.",
        "parameters": [
          {
            "name": "n",
            "in": "query",
            "description": "Number of custom headers to include.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Response with many custom headers.",
            "headers": {
              "X-Chaos-Header-Count": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "endpoint": {
                      "type": "string"
                    },
                    "header_count": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/cache-confused": {
      "parameters": [
        {
          "name": "mode",
          "in": "query",
          "description": "Which contradiction to send. `all` (default) bundles three at once. `store-vs-age` = `no-store, max-age=300`. `cache-vs-immutable` = `no-cache, immutable`. `private-vs-shared` = `private, s-maxage=3600`.\n",
          "schema": {
            "type": "string",
            "enum": [
              "all",
              "store-vs-age",
              "cache-vs-immutable",
              "private-vs-shared"
            ],
            "default": "all"
          }
        }
      ],
      "get": {
        "operationId": "cacheConfused",
        "externalDocs": {
          "description": "Endpoint reference on catastrophic.io",
          "url": "https://catastrophic.io/chaos/endpoints/headers-and-caching/cache-confused/"
        },
        "tags": [
          "headers"
        ],
        "summary": "Contradictory Cache-Control directives",
        "description": "Sends a `Cache-Control` header combining mutually contradictory directives. Tests whether clients, proxies, and CDNs resolve conflicts consistently per RFC 9111. Use `?mode=` to isolate a single contradiction.\n",
        "responses": {
          "200": {
            "description": "Response with a contradictory Cache-Control header.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string"
                }
              },
              "X-Chaos-Payload": {
                "schema": {
                  "type": "string",
                  "const": "cache-confused"
                }
              },
              "X-Chaos-Cache-Mode": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "endpoint": {
                      "type": "string"
                    },
                    "mode": {
                      "type": "string"
                    },
                    "directives_sent": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/drip": {
      "get": {
        "operationId": "drip",
        "externalDocs": {
          "description": "Endpoint reference on catastrophic.io",
          "url": "https://catastrophic.io/chaos/endpoints/latency/drip/"
        },
        "tags": [
          "latency"
        ],
        "summary": "Stream the body slowly in chunks",
        "description": "Headers arrive immediately, but the body trickles in chunks of `chunk` bytes with `interval` ms between each chunk. Unlike `/slow`, the connection stays open and data dribbles in over time.\n\nTotal stream time is `(bytes / chunk) * interval` ms. Keep this under ~25 seconds to stay inside the platform's wall-clock limit.\n",
        "parameters": [
          {
            "name": "bytes",
            "in": "query",
            "description": "Total bytes to stream.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100000,
              "default": 1000
            }
          },
          {
            "name": "interval",
            "in": "query",
            "description": "Milliseconds between chunks.",
            "schema": {
              "type": "integer",
              "minimum": 10,
              "maximum": 5000,
              "default": 200
            }
          },
          {
            "name": "chunk",
            "in": "query",
            "description": "Bytes per chunk.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 4096,
              "default": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Streamed body of the configured size.",
            "headers": {
              "X-Chaos-Drip-Total-Bytes": {
                "schema": {
                  "type": "string"
                }
              },
              "X-Chaos-Drip-Interval-Ms": {
                "schema": {
                  "type": "string"
                }
              },
              "X-Chaos-Drip-Chunk-Bytes": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/rate-limit": {
      "get": {
        "operationId": "rateLimit",
        "externalDocs": {
          "description": "Endpoint reference on catastrophic.io",
          "url": "https://catastrophic.io/chaos/endpoints/rate-limiting/rate-limit/"
        },
        "tags": [
          "rate-limiting"
        ],
        "summary": "Always return 429 with Retry-After",
        "description": "Always returns 429 Too Many Requests. When `lie=true`, the `Retry-After` header is set to `1` regardless of the requested value; the body still reports the real requested value so clients can detect the lie.\n",
        "parameters": [
          {
            "name": "retry_after",
            "in": "query",
            "description": "Retry-After value in seconds.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 3600,
              "default": 60
            }
          },
          {
            "name": "lie",
            "in": "query",
            "description": "If true, send Retry-After=1 but report the real value in the body.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          }
        ],
        "responses": {
          "429": {
            "description": "Rate limited.",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "string"
                }
              },
              "X-RateLimit-Limit": {
                "schema": {
                  "type": "string"
                }
              },
              "X-RateLimit-Remaining": {
                "schema": {
                  "type": "string"
                }
              },
              "X-RateLimit-Reset": {
                "schema": {
                  "type": "string"
                }
              },
              "X-Chaos-Rate-Limit-Lied": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "true",
                    "false"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "endpoint": {
                      "type": "string"
                    },
                    "retry_after_seconds": {
                      "type": "integer"
                    },
                    "header_retry_after": {
                      "type": "integer"
                    },
                    "lied": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/flaky": {
      "get": {
        "operationId": "flaky",
        "externalDocs": {
          "description": "Endpoint reference on catastrophic.io",
          "url": "https://catastrophic.io/chaos/endpoints/fault-injection/flaky/"
        },
        "tags": [
          "fault-injection"
        ],
        "summary": "Random failure at configurable probability",
        "description": "Returns the configured failure status with probability `p`, otherwise 200. The `X-Chaos-Flaky-Failed` header indicates the outcome so monitoring clients can distinguish a chaos failure from a real one.\n\nSet `p=0` to verify retry wiring without triggering failures. Set `p=1` to test the full failure path on every request.\n",
        "parameters": [
          {
            "name": "p",
            "in": "query",
            "description": "Failure probability.",
            "schema": {
              "type": "number",
              "minimum": 0,
              "maximum": 1,
              "default": 0.5
            }
          },
          {
            "name": "status",
            "in": "query",
            "description": "Status code returned on failure.",
            "schema": {
              "type": "integer",
              "minimum": 100,
              "maximum": 599,
              "default": 500
            }
          }
        ],
        "responses": {
          "default": {
            "description": "Either 200 or the configured failure status.",
            "headers": {
              "X-Chaos-Flaky-Failed": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "true",
                    "false"
                  ]
                }
              },
              "X-Chaos-Flaky-Probability": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "endpoint": {
                      "type": "string"
                    },
                    "failed": {
                      "type": "boolean"
                    },
                    "probability": {
                      "type": "number"
                    },
                    "status": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/random": {
      "get": {
        "operationId": "random",
        "externalDocs": {
          "description": "Endpoint reference on catastrophic.io",
          "url": "https://catastrophic.io/chaos/endpoints/fault-injection/random/"
        },
        "tags": [
          "fault-injection"
        ],
        "summary": "Dispatch to a random chaos endpoint",
        "description": "Selects one chaos endpoint at random and dispatches the request to its handler. The selected path (with any query parameters used) is reported in the `X-Chaos-Random-From` response header so failing clients can pin down which scenario tripped them.\n\nStreaming endpoints (`/slow`, `/drip`, `/partial-stream`) are excluded so each pick returns promptly. Useful for soak-testing retry, timeout, and circuit-breaker logic across the whole catalog from a single URL.\n",
        "responses": {
          "default": {
            "description": "Whatever the selected endpoint returned.",
            "headers": {
              "X-Chaos-Random-From": {
                "description": "The path (with query parameters, if any) that was selected.",
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/echo": {
      "parameters": [],
      "get": {
        "operationId": "echoGet",
        "externalDocs": {
          "description": "Endpoint reference on catastrophic.io",
          "url": "https://catastrophic.io/echo/"
        },
        "tags": [
          "inspection"
        ],
        "summary": "Reflect the request as JSON",
        "description": "Returns the request method, URL, path, query parameters, headers, and body as JSON. If the body is valid JSON it is parsed and included as a nested object; otherwise it is included as a raw string.",
        "responses": {
          "200": {
            "$ref": "#/components/responses/EchoResponse"
          }
        }
      },
      "post": {
        "operationId": "echoPost",
        "externalDocs": {
          "description": "Endpoint reference on catastrophic.io",
          "url": "https://catastrophic.io/echo/"
        },
        "tags": [
          "inspection"
        ],
        "summary": "Reflect the request as JSON (POST)",
        "description": "Same as GET; the body is included in the response.",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object"
              }
            },
            "text/plain": {
              "schema": {
                "type": "string"
              }
            }
          }
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/EchoResponse"
          }
        }
      },
      "put": {
        "operationId": "echoPut",
        "externalDocs": {
          "description": "Endpoint reference on catastrophic.io",
          "url": "https://catastrophic.io/echo/"
        },
        "tags": [
          "inspection"
        ],
        "summary": "Reflect the request as JSON (PUT)",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object"
              }
            }
          }
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/EchoResponse"
          }
        }
      },
      "patch": {
        "operationId": "echoPatch",
        "externalDocs": {
          "description": "Endpoint reference on catastrophic.io",
          "url": "https://catastrophic.io/echo/"
        },
        "tags": [
          "inspection"
        ],
        "summary": "Reflect the request as JSON (PATCH)",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object"
              }
            }
          }
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/EchoResponse"
          }
        }
      },
      "delete": {
        "operationId": "echoDelete",
        "externalDocs": {
          "description": "Endpoint reference on catastrophic.io",
          "url": "https://catastrophic.io/echo/"
        },
        "tags": [
          "inspection"
        ],
        "summary": "Reflect the request as JSON (DELETE)",
        "responses": {
          "200": {
            "$ref": "#/components/responses/EchoResponse"
          }
        }
      }
    },
    "/auth-required": {
      "get": {
        "operationId": "authRequired",
        "externalDocs": {
          "description": "Endpoint reference on catastrophic.io",
          "url": "https://catastrophic.io/chaos/endpoints/auth/auth-required/"
        },
        "tags": [
          "auth"
        ],
        "summary": "401 with WWW-Authenticate challenge",
        "description": "Returns 401 with a `WWW-Authenticate` challenge when no `Authorization` header is present. If any `Authorization` header is sent — regardless of its value — returns 200. This lets clients exercise the full challenge-response cycle without real credentials.\n",
        "parameters": [
          {
            "name": "scheme",
            "in": "query",
            "description": "Challenge scheme. The `none` value returns 401 with no `WWW-Authenticate` header.",
            "schema": {
              "type": "string",
              "enum": [
                "basic",
                "bearer",
                "digest",
                "none"
              ],
              "default": "basic"
            }
          },
          {
            "name": "realm",
            "in": "query",
            "description": "Realm value for the challenge. Sanitized to safe characters and capped at 64 chars.",
            "schema": {
              "type": "string",
              "default": "catastrophic.io"
            }
          },
          {
            "name": "Authorization",
            "in": "header",
            "required": false,
            "description": "Send any value to get a 200 instead of a 401.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Returned when any Authorization header is present.",
            "headers": {
              "X-Chaos-Auth-Accepted": {
                "schema": {
                  "type": "string",
                  "const": "true"
                }
              },
              "X-Chaos-Auth-Scheme": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "endpoint": {
                      "type": "string",
                      "const": "/auth-required"
                    },
                    "authenticated": {
                      "type": "boolean",
                      "const": true
                    },
                    "scheme": {
                      "type": "string"
                    },
                    "received_authorization": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Returned when no Authorization header is present.",
            "headers": {
              "WWW-Authenticate": {
                "description": "Present unless scheme=none.",
                "schema": {
                  "type": "string"
                }
              },
              "X-Chaos-Auth-Scheme": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "endpoint": {
                      "type": "string",
                      "const": "/auth-required"
                    },
                    "authenticated": {
                      "type": "boolean",
                      "const": false
                    },
                    "scheme": {
                      "type": "string"
                    },
                    "challenge": {
                      "oneOf": [
                        {
                          "type": "string"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "hint": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/cors-broken": {
      "parameters": [
        {
          "name": "mode",
          "in": "query",
          "description": "Brokenness mode.",
          "schema": {
            "type": "string",
            "enum": [
              "missing",
              "wildcard-credentials",
              "wrong-origin",
              "preflight-deny"
            ],
            "default": "missing"
          }
        }
      ],
      "get": {
        "operationId": "corsBrokenGet",
        "externalDocs": {
          "description": "Endpoint reference on catastrophic.io",
          "url": "https://catastrophic.io/chaos/endpoints/cors/cors-broken/"
        },
        "tags": [
          "cors"
        ],
        "summary": "Send broken or missing CORS headers",
        "description": "Returns 200 with CORS headers (or none) according to the chosen mode. Browsers will block cross-origin reads from all modes — that is the point. Direct (non-browser) clients see the headers but do not enforce CORS.\n",
        "responses": {
          "200": {
            "description": "The actual response. CORS headers vary by mode.",
            "headers": {
              "Access-Control-Allow-Origin": {
                "description": "Present in wildcard-credentials, wrong-origin, and preflight-deny modes.",
                "schema": {
                  "type": "string"
                }
              },
              "Access-Control-Allow-Credentials": {
                "description": "Only present in wildcard-credentials mode.",
                "schema": {
                  "type": "string"
                }
              },
              "Access-Control-Allow-Methods": {
                "schema": {
                  "type": "string"
                }
              },
              "X-Chaos-Cors-Mode": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "endpoint": {
                      "type": "string",
                      "const": "/cors-broken"
                    },
                    "mode": {
                      "type": "string"
                    },
                    "request_origin": {
                      "type": "string"
                    },
                    "cors_headers_sent": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "options": {
        "operationId": "corsBrokenPreflight",
        "externalDocs": {
          "description": "Endpoint reference on catastrophic.io",
          "url": "https://catastrophic.io/chaos/endpoints/cors/cors-broken/"
        },
        "tags": [
          "cors"
        ],
        "summary": "Handle CORS preflight",
        "description": "Returns 204 in most modes. In `preflight-deny` mode, returns 403 and the actual request is blocked by the browser before it ever fires.\n",
        "responses": {
          "204": {
            "description": "Preflight succeeded (CORS headers vary by mode).",
            "headers": {
              "Access-Control-Allow-Origin": {
                "schema": {
                  "type": "string"
                }
              },
              "Access-Control-Allow-Methods": {
                "schema": {
                  "type": "string"
                }
              },
              "X-Chaos-Cors-Mode": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "403": {
            "description": "Preflight denied (only in preflight-deny mode).",
            "headers": {
              "X-Chaos-Cors-Mode": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/semantic-drift": {
      "get": {
        "operationId": "semanticDrift",
        "externalDocs": {
          "description": "Endpoint reference on catastrophic.io",
          "url": "https://catastrophic.io/chaos/endpoints/agent/semantic-drift/"
        },
        "tags": [
          "agent"
        ],
        "summary": "Structurally-valid JSON whose values lie about reality",
        "description": "Returns a JSON response matching the shape of a well-known discovery\nschema (openid-configuration, webfinger, jwks, or agent-card), where\nevery URL points at a non-existent resource and every claimed\ncapability is fabricated. Passes structural validators; fails any\nconsumer that resolves the values.\n",
        "parameters": [
          {
            "name": "schema",
            "in": "query",
            "description": "Which schema shape to return.",
            "schema": {
              "type": "string",
              "enum": [
                "openid-configuration",
                "oauth-authorization-server",
                "webfinger",
                "jwks",
                "host-meta",
                "agent-card"
              ],
              "default": "openid-configuration"
            }
          },
          {
            "name": "ai",
            "in": "query",
            "description": "If `true`, the response body is generated fresh on every call by an edge LLM (Llama-3.1-8b) — same shape, different invented values each time. If `false` (default), the response is the deterministic hardcoded drift.\n\nAI mode falls back to the deterministic drift automatically when the inference backend is unavailable, the shared daily budget is exhausted (`ai_state >= 2`), the model errors, or returns unparseable JSON. The `X-Chaos-Ai-Source` response header indicates which path served the request (`ai` or `fallback`); on fallback, `X-Chaos-Ai-Fallback-Reason` explains why.\n\nAI calls add 100ms–10s of latency; first-call-after-cold-load is the slow case.\n",
            "schema": {
              "type": "boolean",
              "default": false
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Structurally-valid drifted response. Source indicated by X-Chaos-Ai-Source header.",
            "headers": {
              "X-Chaos-Schema": {
                "schema": {
                  "type": "string"
                }
              },
              "X-Chaos-Drift": {
                "schema": {
                  "type": "string",
                  "const": "semantic"
                }
              },
              "X-Chaos-Hint": {
                "schema": {
                  "type": "string"
                }
              },
              "X-Chaos-Ai-Source": {
                "description": "Present only when `ai=true`. Either `ai` (model-generated) or `fallback` (deterministic).",
                "schema": {
                  "type": "string",
                  "enum": [
                    "ai",
                    "fallback"
                  ]
                }
              },
              "X-Chaos-Ai-Model": {
                "description": "Present only on successful AI generation.",
                "schema": {
                  "type": "string"
                }
              },
              "X-Chaos-Ai-Latency-Ms": {
                "description": "AI invocation duration. Present when AI was attempted.",
                "schema": {
                  "type": "string"
                }
              },
              "X-Chaos-Ai-Fallback-Reason": {
                "description": "Present only on fallback. Values include `ai-binding-missing`, `budget-exhausted`, `ai-error`, `invalid-json`, `no-prompt-for-schema`.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              },
              "application/jrd+json": {
                "schema": {
                  "type": "object"
                }
              },
              "application/jwk-set+json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Returned when the requested schema is not one of the supported values.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "endpoint": {
                      "type": "string"
                    },
                    "error": {
                      "type": "string"
                    },
                    "requested": {
                      "type": "string"
                    },
                    "valid": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/retry-mismatch": {
      "get": {
        "operationId": "retryMismatch",
        "externalDocs": {
          "description": "Endpoint reference on catastrophic.io",
          "url": "https://catastrophic.io/chaos/endpoints/agent/retry-mismatch/"
        },
        "tags": [
          "agent"
        ],
        "summary": "Different response on retry within 60s",
        "description": "Token-keyed retry chaos. The first call for a given `id` returns version 1 of the response; any subsequent call for the same `id` within a 60-second window returns version 2 — same response shape, different data. Tests whether retry logic silently picks up changed data without noticing the shift.\n\nState is per-edge (in-memory cache) and expires after 60 seconds.\nUse any string as `id` — uuids, request IDs, anything stable across the retry.\n",
        "parameters": [
          {
            "name": "id",
            "in": "query",
            "required": true,
            "description": "Client-supplied identity token (1–128 chars). State is tracked per id.",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 128
            }
          }
        ],
        "responses": {
          "200": {
            "description": "First call returns version-1 data; subsequent calls within 60s for the same id return version-2 data. Check `X-Chaos-Retry-Replayed` (`true`/`false`) and `data.version` (1 or 2) to disambiguate.\n",
            "headers": {
              "X-Chaos-Retry-Attempt": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "1",
                    "2+"
                  ]
                }
              },
              "X-Chaos-Retry-Replayed": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "true",
                    "false"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "endpoint": {
                      "type": "string",
                      "const": "/retry-mismatch"
                    },
                    "id": {
                      "type": "string"
                    },
                    "attempt": {
                      "type": "string"
                    },
                    "replayed": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "items": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "version": {
                          "type": "integer",
                          "enum": [
                            1,
                            2
                          ]
                        },
                        "generated_at": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Returned when `id` is missing or longer than 128 chars.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "endpoint": {
                      "type": "string"
                    },
                    "error": {
                      "type": "string"
                    },
                    "hint": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/partial-stream": {
      "get": {
        "operationId": "partialStream",
        "externalDocs": {
          "description": "Endpoint reference on catastrophic.io",
          "url": "https://catastrophic.io/chaos/endpoints/delivery/partial-stream/"
        },
        "tags": [
          "agent"
        ],
        "summary": "Cleanly close the stream mid-JSON",
        "description": "Sends `Content-Type: application/json` and a JSON fragment, then closes the stream after the configured byte count. No `Content-Length` is sent — the HTTP layer reports success and the client sees a well-formed-but-incomplete body. Tests partial-parse recovery and timeout handling.\n",
        "parameters": [
          {
            "name": "bytes",
            "in": "query",
            "description": "How many bytes of the JSON fragment to send before closing.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 16384,
              "default": 128
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Incomplete JSON body. HTTP layer is healthy; the payload is not.",
            "headers": {
              "X-Chaos-Partial-Stream-Bytes": {
                "schema": {
                  "type": "string"
                }
              },
              "X-Chaos-Hint": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "string",
                  "description": "Not actually valid JSON — body is cut off mid-object."
                }
              }
            }
          }
        }
      }
    },
    "/.well-known/chaos": {
      "get": {
        "operationId": "wellKnownChaos",
        "externalDocs": {
          "description": "Endpoint reference on catastrophic.io",
          "url": "https://catastrophic.io/chaos/endpoints/discovery/well-known-chaos/"
        },
        "tags": [
          "discovery"
        ],
        "summary": "Mutated .well-known discovery document",
        "description": "Parametric chaos for well-known discovery schemas. Schema (openid-configuration, webfinger, jwks, agent-card) × mode (semantic, missing-fields, wrong-types). Response is served with the correct Content-Type per schema.\n",
        "parameters": [
          {
            "name": "schema",
            "in": "query",
            "description": "Which schema shape to base the response on.",
            "schema": {
              "type": "string",
              "enum": [
                "openid-configuration",
                "oauth-authorization-server",
                "webfinger",
                "jwks",
                "host-meta",
                "agent-card"
              ],
              "default": "openid-configuration"
            }
          },
          {
            "name": "mode",
            "in": "query",
            "description": "How to misbehave.\n`semantic` keeps the shape valid but URLs unreachable;\n`missing-fields` strips required fields;\n`wrong-types` shape-shifts a field type.\n",
            "schema": {
              "type": "string",
              "enum": [
                "semantic",
                "missing-fields",
                "wrong-types"
              ],
              "default": "semantic"
            }
          },
          {
            "name": "ai",
            "in": "query",
            "description": "If `true`, the source data is generated fresh on every call by an edge LLM (Llama-3.1-8b), then the requested `mode` transformation is applied on top. So `ai=true&mode=missing-fields` returns an AI-invented agent or issuer with required fields then stripped.\n\nFalls back to the deterministic source when the inference backend is missing, the shared daily budget is exhausted, the model errors, or returns unparseable JSON. The `X-Chaos-Ai-Source` response header indicates which path served; on fallback, `X-Chaos-Ai-Fallback-Reason` explains why.\n\nAdds 100ms–10s of latency per call.\n",
            "schema": {
              "type": "boolean",
              "default": false
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Drifted discovery document.",
            "headers": {
              "X-Chaos-Schema": {
                "schema": {
                  "type": "string"
                }
              },
              "X-Chaos-Drift": {
                "schema": {
                  "type": "string"
                }
              },
              "X-Chaos-Ai-Source": {
                "description": "Present only when `ai=true`. Either `ai` or `fallback`.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "ai",
                    "fallback"
                  ]
                }
              },
              "X-Chaos-Ai-Model": {
                "description": "Present only on successful AI generation.",
                "schema": {
                  "type": "string"
                }
              },
              "X-Chaos-Ai-Latency-Ms": {
                "description": "AI invocation duration. Present when AI was attempted.",
                "schema": {
                  "type": "string"
                }
              },
              "X-Chaos-Ai-Fallback-Reason": {
                "description": "Present only on fallback. Codes include `ai-binding-missing`, `budget-exhausted`, `ai-error`, `invalid-json`, `no-prompt-for-schema`.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              },
              "application/jrd+json": {
                "schema": {
                  "type": "object"
                }
              },
              "application/jwk-set+json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Returned when schema or mode is not one of the supported values.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "endpoint": {
                      "type": "string"
                    },
                    "error": {
                      "type": "string"
                    },
                    "requested": {
                      "type": "string"
                    },
                    "valid": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/cookie": {
      "get": {
        "operationId": "cookieChaos",
        "externalDocs": {
          "description": "Endpoint reference on catastrophic.io",
          "url": "https://catastrophic.io/chaos/endpoints/headers-and-caching/cookie/"
        },
        "tags": [
          "headers"
        ],
        "summary": "Malformed Set-Cookie headers",
        "description": "Returns one or more Set-Cookie headers crafted to be silently dropped, contradict themselves, or exceed practical browser limits. Tests whether your code assumes Set-Cookie succeeded or actually verifies session establishment on the next request.\n",
        "parameters": [
          {
            "name": "mode",
            "in": "query",
            "description": "`samesite-none-no-secure` (default) — SameSite=None without Secure; modern browsers reject silently.\n`expiry-contradiction` — past Expires with far-future Max-Age; RFC 6265 says Max-Age wins but not every client knows that.\n`multiple-same-name` — three Set-Cookie headers with the same name; browsers usually keep the last.\n`bad-path` — cookie scoped to a path the response was not served from; clients silently drop it.\n`huge-value` — ~8KB value, over the 4KB practical limit; browsers and proxies drop the entire header.\n`host-prefix-violation` — `__Host-` cookie with Domain= set, violating RFC 6265bis §4.1.3.2; browsers reject silently.\n`secure-prefix-violation` — `__Secure-` cookie without the Secure attribute, violating RFC 6265bis §4.1.3.1; browsers reject silently.\n`comma-folded` — two cookies joined in one Set-Cookie value with a comma; RFC 6265 §4.1 forbids combining Set-Cookie headers this way — intermediaries that fold anyway break both cookies.\n`domain-overreach` — Domain=catastrophic.io from chaos.catastrophic.io; RFC 6265 §5.3.6 requires host-match.\n`partitioned-no-secure` — Partitioned (CHIPS) without Secure; CHIPS spec requires Secure; browsers with CHIPS support reject silently.\n",
            "schema": {
              "type": "string",
              "enum": [
                "samesite-none-no-secure",
                "expiry-contradiction",
                "multiple-same-name",
                "bad-path",
                "huge-value",
                "host-prefix-violation",
                "secure-prefix-violation",
                "comma-folded",
                "domain-overreach",
                "partitioned-no-secure"
              ],
              "default": "samesite-none-no-secure"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Always 200.",
            "headers": {
              "X-Chaos-Cookie-Mode": {
                "schema": {
                  "type": "string"
                }
              },
              "Set-Cookie": {
                "description": "One or more cookie strings, varying by mode.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "endpoint": {
                      "type": "string",
                      "const": "/cookie"
                    },
                    "mode": {
                      "type": "string"
                    },
                    "note": {
                      "type": "string"
                    },
                    "set_cookie_lines": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/etag": {
      "get": {
        "operationId": "etagChaos",
        "externalDocs": {
          "description": "Endpoint reference on catastrophic.io",
          "url": "https://catastrophic.io/chaos/endpoints/headers-and-caching/etag/"
        },
        "tags": [
          "headers"
        ],
        "summary": "ETag and Last-Modified headers that break conditional requests",
        "description": "Returns ETag and Last-Modified headers that violate RFC 9110 in specific ways. Tests caches, CDNs, and clients that rely on validators.\n",
        "parameters": [
          {
            "name": "mode",
            "in": "query",
            "description": "`random-etag` (default) — different ETag every call for identical content.\n`stable-etag-mutated-body` — same ETag, body changes.\n`weak-strong-conflict` — both strong and weak ETag headers.\n`unquoted` — ETag without required quotes.\n`future-last-modified` — Last-Modified in 2099.\n",
            "schema": {
              "type": "string",
              "enum": [
                "random-etag",
                "stable-etag-mutated-body",
                "weak-strong-conflict",
                "unquoted",
                "future-last-modified"
              ],
              "default": "random-etag"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Always 200.",
            "headers": {
              "X-Chaos-Etag-Mode": {
                "schema": {
                  "type": "string"
                }
              },
              "ETag": {
                "schema": {
                  "type": "string"
                }
              },
              "Last-Modified": {
                "description": "Present only in `future-last-modified` mode.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "endpoint": {
                      "type": "string",
                      "const": "/etag-chaos"
                    },
                    "mode": {
                      "type": "string"
                    },
                    "note": {
                      "type": "string"
                    },
                    "body": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/pagination": {
      "get": {
        "operationId": "paginationChaos",
        "externalDocs": {
          "description": "Endpoint reference on catastrophic.io",
          "url": "https://catastrophic.io/chaos/endpoints/pagination/pagination/"
        },
        "tags": [
          "pagination"
        ],
        "summary": "Paginated collections whose metadata lies",
        "description": "Returns paginated collections where `total_items` lies, pages overlap, `Link` headers loop back to page 1, or returned size drifts from requested size. Walk pages 1–5 with the same mode to see the chaos accumulate.\n",
        "parameters": [
          {
            "name": "mode",
            "in": "query",
            "description": "`lying-total` (default) — claims 10 pages, only pages 1–3 return items.\n`overlapping-pages` — consecutive pages share 5 items.\n`link-loop` — Link rel=next points back to page 1.\n`size-drift` — returned size does not match requested size.\n",
            "schema": {
              "type": "string",
              "enum": [
                "lying-total",
                "overlapping-pages",
                "link-loop",
                "size-drift"
              ],
              "default": "lying-total"
            }
          },
          {
            "name": "page",
            "in": "query",
            "description": "Page number, 1-indexed.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 20,
              "default": 1
            }
          },
          {
            "name": "size",
            "in": "query",
            "description": "Requested items per page. Some modes ignore this.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 10
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Always 200.",
            "headers": {
              "X-Chaos-Pagination-Mode": {
                "schema": {
                  "type": "string"
                }
              },
              "Link": {
                "description": "Present in `link-loop` mode. Contains rel=\"next\" and rel=\"last\" links.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "endpoint": {
                      "type": "string",
                      "const": "/pagination-chaos"
                    },
                    "mode": {
                      "type": "string"
                    },
                    "note": {
                      "type": "string"
                    },
                    "page": {
                      "type": "integer"
                    },
                    "requested_size": {
                      "type": "integer"
                    },
                    "returned_size": {
                      "type": "integer"
                    },
                    "total_items": {
                      "type": "integer"
                    },
                    "total_pages": {
                      "type": "integer"
                    },
                    "items": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "integer"
                          },
                          "name": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/compression": {
      "get": {
        "operationId": "compressionChaos",
        "externalDocs": {
          "description": "Endpoint reference on catastrophic.io",
          "url": "https://catastrophic.io/chaos/endpoints/headers-and-caching/compression/"
        },
        "tags": [
          "headers"
        ],
        "summary": "Lying Content-Encoding or unannounced compression",
        "description": "Returns responses where `Content-Encoding` lies about the body's actual compression state, or where the body is gzip-compressed but the header doesn't say so. Tests how clients, caches, and proxies handle disagreement between encoding metadata and bytes.\n\nAll modes set `Cache-Control: no-transform` so the Cloudflare edge does not transparently re-encode the response. Note: clients that do NOT send `Accept-Encoding: gzip` will have the lying `Content-Encoding` header stripped by content negotiation at the edge.\n",
        "parameters": [
          {
            "name": "mode",
            "in": "query",
            "description": "`gzip-header-plain-body` (default) — Content-Encoding: gzip on a plain JSON body.\n`gzip-body-no-header` — body is actually gzipped; no Content-Encoding header.\n`wrong-encoding-name` — Content-Encoding: superzip, an unknown encoding.\n`chained-claim` — Content-Encoding: gzip, br on a plain body.\n",
            "schema": {
              "type": "string",
              "enum": [
                "gzip-header-plain-body",
                "gzip-body-no-header",
                "wrong-encoding-name",
                "chained-claim"
              ],
              "default": "gzip-header-plain-body"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Always 200.",
            "headers": {
              "X-Chaos-Compression-Mode": {
                "schema": {
                  "type": "string"
                }
              },
              "Content-Encoding": {
                "description": "Present (and lying) in all modes except `gzip-body-no-header`.",
                "schema": {
                  "type": "string"
                }
              },
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-transform"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "Plain JSON body, except in `gzip-body-no-header` mode where the body is real gzip bytes."
                }
              }
            }
          }
        }
      }
    },
    "/auth": {
      "get": {
        "operationId": "authChaos",
        "externalDocs": {
          "description": "Endpoint reference on catastrophic.io",
          "url": "https://catastrophic.io/chaos/endpoints/auth/auth/"
        },
        "tags": [
          "auth"
        ],
        "summary": "Malformed WWW-Authenticate challenges",
        "description": "Returns 401 with `WWW-Authenticate` challenges that contradict each other, omit required attributes, violate quoting rules, or use undefined schemes. Tests how auth clients cope when the challenge itself is broken.\n",
        "parameters": [
          {
            "name": "mode",
            "in": "query",
            "description": "`multiple-challenges` (default) — three contradictory challenges (Basic + Bearer + Digest) with different realms.\n`malformed-digest` — Digest scheme missing required nonce, qop, opaque.\n`unquoted-realm` — realm value without RFC 9110 quoted-string wrapping.\n`nonexistent-scheme` — challenge uses an undefined auth scheme name.\n",
            "schema": {
              "type": "string",
              "enum": [
                "multiple-challenges",
                "malformed-digest",
                "unquoted-realm",
                "nonexistent-scheme"
              ],
              "default": "multiple-challenges"
            }
          }
        ],
        "responses": {
          "401": {
            "description": "Always 401.",
            "headers": {
              "X-Chaos-Auth-Mode": {
                "schema": {
                  "type": "string"
                }
              },
              "WWW-Authenticate": {
                "description": "One or more challenge strings, varying by mode.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "endpoint": {
                      "type": "string",
                      "const": "/auth-chaos"
                    },
                    "mode": {
                      "type": "string"
                    },
                    "note": {
                      "type": "string"
                    },
                    "challenges": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/method": {
      "get": {
        "operationId": "methodChaos",
        "externalDocs": {
          "description": "Endpoint reference on catastrophic.io",
          "url": "https://catastrophic.io/chaos/endpoints/method-handling/method/"
        },
        "tags": [
          "method"
        ],
        "summary": "Servers that lie about which methods they support",
        "description": "An endpoint that claims via Allow or OPTIONS to support GET, POST, PUT, PATCH, DELETE, OPTIONS but actually handles only GET. The chaos surfaces when non-GET methods are sent. GET always returns 200 with metadata describing the mode so the try-it experience still shows context without firing the non-GET methods.\n",
        "parameters": [
          {
            "name": "mode",
            "in": "query",
            "description": "`lying-allow` (default) — Allow header claims six methods; only GET works.\n`silent-on-wrong-method` — non-GET returns 200 with empty body.\n`405-without-allow` — 405 with no Allow header (RFC 9110 violation).\n`options-mismatch` — OPTIONS advertises six methods; only GET actually works.\n",
            "schema": {
              "type": "string",
              "enum": [
                "lying-allow",
                "silent-on-wrong-method",
                "405-without-allow",
                "options-mismatch"
              ],
              "default": "lying-allow"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "GET returns 200 with metadata.",
            "headers": {
              "X-Chaos-Method-Mode": {
                "schema": {
                  "type": "string"
                }
              },
              "X-Chaos-Method-Claimed": {
                "schema": {
                  "type": "string"
                }
              },
              "X-Chaos-Method-Actual": {
                "schema": {
                  "type": "string"
                }
              },
              "Allow": {
                "description": "Present in `lying-allow` and `options-mismatch` modes.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "204": {
            "description": "OPTIONS responses use 204 No Content."
          },
          "405": {
            "description": "Non-GET methods in modes where they return Method Not Allowed.",
            "headers": {
              "X-Chaos-Method-Mode": {
                "schema": {
                  "type": "string"
                }
              },
              "Allow": {
                "description": "Present in `lying-allow` and `options-mismatch` modes; absent (RFC violation) in `405-without-allow`.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/image": {
      "get": {
        "operationId": "imageChaos",
        "externalDocs": {
          "description": "Endpoint reference on catastrophic.io",
          "url": "https://catastrophic.io/chaos/endpoints/format/image/"
        },
        "tags": [
          "payload"
        ],
        "summary": "Image responses whose metadata lies about the bytes",
        "description": "Returns an image-shaped body where the declared MIME type, the magic bytes, the embedded IHDR dimensions, or the IDAT chunk framing disagrees with the actual content. Tests how image-processing pipelines handle the case where wire-level metadata and pixel data tell different stories. The `X-Chaos-Image-Note` header explains the lie in plain text. `Cache-Control: no-transform` is set so the Cloudflare edge does not re-encode or content-sniff the response and mask the chaos.\n",
        "parameters": [
          {
            "name": "mode",
            "in": "query",
            "description": "`mime-mismatch` (default) — real PNG bytes served with `Content-Type: image/jpeg`.\n`magic-byte-lie` — PNG signature prefix on a JPEG body; magic-number sniffers say PNG, decoders fail past byte 8.\n`wrong-dimensions` — valid 1x1 PNG whose IHDR claims 4000x4000 (CRC is correct). `X-Chaos-Image-Claimed-Dimensions` and `X-Chaos-Image-Actual-Dimensions` headers carry the contradiction.\n`truncated-png` — IHDR + an IDAT chunk that declares 100 bytes but ships ~9, with no IEND.\n",
            "schema": {
              "type": "string",
              "enum": [
                "mime-mismatch",
                "magic-byte-lie",
                "wrong-dimensions",
                "truncated-png"
              ],
              "default": "mime-mismatch"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Image-shaped body whose metadata lies. Body Content-Type varies by mode (image/png or image/jpeg).",
            "headers": {
              "X-Chaos-Image-Mode": {
                "schema": {
                  "type": "string"
                }
              },
              "X-Chaos-Image-Note": {
                "description": "Plain-text explanation of the lie under test.",
                "schema": {
                  "type": "string"
                }
              },
              "X-Chaos-Image-Claimed-Dimensions": {
                "description": "Present only in `wrong-dimensions` mode.",
                "schema": {
                  "type": "string"
                }
              },
              "X-Chaos-Image-Actual-Dimensions": {
                "description": "Present only in `wrong-dimensions` mode.",
                "schema": {
                  "type": "string"
                }
              },
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-transform"
                }
              }
            },
            "content": {
              "image/png": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "image/jpeg": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          }
        }
      }
    },
    "/jsonl": {
      "get": {
        "operationId": "jsonlChaos",
        "externalDocs": {
          "description": "Endpoint reference on catastrophic.io",
          "url": "https://catastrophic.io/chaos/endpoints/format/jsonl/"
        },
        "tags": [
          "payload"
        ],
        "summary": "JSON Lines streams with stream-level violations",
        "description": "Returns a JSON Lines / NDJSON stream with a deliberate stream-level flaw. Targets ML pipelines, log aggregators, and AI chat exports — anywhere line-delimited JSON is consumed. `Cache-Control: no-transform` is set so the Cloudflare edge does not rewrite the body. `X-Chaos-Jsonl-Note` explains the flaw in plain text.\n",
        "parameters": [
          {
            "name": "mode",
            "in": "query",
            "description": "`schema-drift` (default) — records 1–3 use one shape, records 4–5 use a different shape; schema-aware pipelines reject or null-coerce.\n`blank-lines` — empty lines interleaved between records; strict per-line parsers raise on the empty string.\n`partial-final-line` — final record truncated mid-object with no trailing newline; streaming parsers raise on EOF mid-record.\n`bom-start` — UTF-8 BOM at start of stream; strict per-line parsers raise on the first record only.\n",
            "schema": {
              "type": "string",
              "enum": [
                "schema-drift",
                "blank-lines",
                "partial-final-line",
                "bom-start"
              ],
              "default": "schema-drift"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "NDJSON stream with the chosen flaw.",
            "headers": {
              "X-Chaos-Jsonl-Mode": {
                "schema": {
                  "type": "string"
                }
              },
              "X-Chaos-Jsonl-Note": {
                "description": "Plain-text explanation of the flaw under test.",
                "schema": {
                  "type": "string"
                }
              },
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-transform"
                }
              }
            },
            "content": {
              "application/x-ndjson": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/svg": {
      "get": {
        "operationId": "svgChaos",
        "externalDocs": {
          "description": "Endpoint reference on catastrophic.io",
          "url": "https://catastrophic.io/chaos/endpoints/format/svg/"
        },
        "tags": [
          "payload"
        ],
        "summary": "SVG documents with structural and rendering chaos",
        "description": "Returns SVG with a deliberate flaw. SVG is an XML+rendering hybrid so chaos modes span both worlds: malformed XML, external-resource references, recursive <use> chains, non-conformant namespaces. `Cache-Control: no-transform` is set so the Cloudflare edge does not rewrite the body. `X-Chaos-Svg-Note` explains the flaw in plain text.\n",
        "parameters": [
          {
            "name": "mode",
            "in": "query",
            "description": "`mismatched-tags` (default) — `<g>` closes before its `<rect>` child closes; XML parsers reject, lenient renderers recover.\n`external-image-ref` — embedded `<image href=\"http://nonexistent.invalid/...\">`; processors that resolve external refs hang or fail.\n`circular-use` — two `<use>` elements reference each other; non-conformant renderers may recurse.\n`bad-namespace` — xmlns declares a non-SVG namespace; namespace-aware processors refuse to render.\n",
            "schema": {
              "type": "string",
              "enum": [
                "mismatched-tags",
                "external-image-ref",
                "circular-use",
                "bad-namespace"
              ],
              "default": "mismatched-tags"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "SVG body with the chosen flaw.",
            "headers": {
              "X-Chaos-Svg-Mode": {
                "schema": {
                  "type": "string"
                }
              },
              "X-Chaos-Svg-Note": {
                "description": "Plain-text explanation of the flaw under test.",
                "schema": {
                  "type": "string"
                }
              },
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-transform"
                }
              }
            },
            "content": {
              "image/svg+xml": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/csv": {
      "get": {
        "operationId": "csvChaos",
        "externalDocs": {
          "description": "Endpoint reference on catastrophic.io",
          "url": "https://catastrophic.io/chaos/endpoints/format/csv/"
        },
        "tags": [
          "payload"
        ],
        "summary": "CSV with RFC 4180 corner-case violations",
        "description": "Returns CSV with a deliberate RFC 4180 violation. Targets data pipelines, BI tools, and ML preprocessing — anywhere CSV is the interchange format and parsers vary in strictness. `Cache-Control: no-transform` is set so the Cloudflare edge does not rewrite the body. `X-Chaos-Csv-Note` explains the flaw in plain text.\n",
        "parameters": [
          {
            "name": "mode",
            "in": "query",
            "description": "`unquoted-commas` (default) — a field contains a comma but isn't quoted; the row appears to have 4 columns instead of 3.\n`embedded-newlines` — a row contains a literal CRLF inside an unquoted field; naive line-splitters break mid-row.\n`bom-mismatch` — UTF-8 BOM at the start of the stream; strict parsers include the BOM bytes in cell[0] of the header row.\n`ragged-columns` — header declares 3 columns but rows carry 2 and 4; strict parsers raise, lenient ones pad or misalign silently.\n",
            "schema": {
              "type": "string",
              "enum": [
                "unquoted-commas",
                "embedded-newlines",
                "bom-mismatch",
                "ragged-columns"
              ],
              "default": "unquoted-commas"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "CSV body with the chosen RFC 4180 violation.",
            "headers": {
              "X-Chaos-Csv-Mode": {
                "schema": {
                  "type": "string"
                }
              },
              "X-Chaos-Csv-Note": {
                "description": "Plain-text explanation of the flaw under test.",
                "schema": {
                  "type": "string"
                }
              },
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-transform"
                }
              }
            },
            "content": {
              "text/csv": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/xml": {
      "get": {
        "operationId": "xmlChaos",
        "externalDocs": {
          "description": "Endpoint reference on catastrophic.io",
          "url": "https://catastrophic.io/chaos/endpoints/format/xml/"
        },
        "tags": [
          "payload"
        ],
        "summary": "XML documents with parser-attack or well-formedness flaws",
        "description": "Returns XML with a deliberate structural flaw. Targets SOAP, B2B, RSS/sitemap, and config-file ingestion pipelines. Default mode is visible misnesting on a bare GET. `Cache-Control: no-transform` is set so the Cloudflare edge does not rewrite the body. `X-Chaos-Xml-Note` explains the flaw in plain text.\n",
        "parameters": [
          {
            "name": "mode",
            "in": "query",
            "description": "`mismatched-tags` (default) — `<alpha><beta></alpha></beta>`; every conformant parser rejects.\n`billion-laughs` — three levels of self-similar entity expansion (~3KB expanded); hardened parsers reject, vulnerable ones expand.\n`xxe-external` — external entity referencing nonexistent.invalid (RFC 6761 reserved); parsers with external-entity resolution hang or fail.\n`encoding-mismatch` — declaration claims utf-8 but body bytes are CP1252.\n",
            "schema": {
              "type": "string",
              "enum": [
                "mismatched-tags",
                "billion-laughs",
                "xxe-external",
                "encoding-mismatch"
              ],
              "default": "mismatched-tags"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "XML body with the chosen flaw.",
            "headers": {
              "X-Chaos-Xml-Mode": {
                "schema": {
                  "type": "string"
                }
              },
              "X-Chaos-Xml-Note": {
                "description": "Plain-text explanation of the flaw under test.",
                "schema": {
                  "type": "string"
                }
              },
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-transform"
                }
              }
            },
            "content": {
              "application/xml": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/zip": {
      "get": {
        "operationId": "zipChaos",
        "externalDocs": {
          "description": "Endpoint reference on catastrophic.io",
          "url": "https://catastrophic.io/chaos/endpoints/format/zip/"
        },
        "tags": [
          "payload"
        ],
        "summary": "ZIP archives with one deliberate structural lie",
        "description": "Returns a single-entry STORED ZIP archive where one structural field disagrees with the bytes. Targets archive extractors, package managers, document-format parsers (DOCX/XLSX/PPTX are ZIPs under the hood), and security scanners. `Cache-Control: no-transform` is set so the Cloudflare edge does not rewrite the body. `X-Chaos-Zip-Note` explains the flaw in plain text. The famously dangerous zip-slip (path-traversal payload) and zip-bomb (resource-exhaustion payload) shapes are deliberately not served.\n",
        "parameters": [
          {
            "name": "mode",
            "in": "query",
            "description": "`bad-crc` (default) — LFH and CDH declare CRC32 0xDEADBEEF; the body's real CRC differs. Strict extractors reject; lenient ones extract anyway.\n`central-dir-mismatch` — local file header names `alpha.txt`, central directory names `beta.txt` for the same offset and body. CD-driven tools and LFH-scanning tools disagree about archive contents.\n`zip64-lie` — LFH/CDH sizes use the 0xFFFFFFFF ZIP64 sentinel; the ZIP64 Extended Information extra field claims 8 GiB. Actual body is ~40 bytes.\n`truncated` — LFH and body intact; the central directory is cut off mid-record and no EOCD is emitted. EOCD-first parsers fail; LFH-scanning parsers can still recover the file.\n",
            "schema": {
              "type": "string",
              "enum": [
                "bad-crc",
                "central-dir-mismatch",
                "zip64-lie",
                "truncated"
              ],
              "default": "bad-crc"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "ZIP-shaped body whose structure contains one deliberate lie.",
            "headers": {
              "X-Chaos-Zip-Mode": {
                "schema": {
                  "type": "string"
                }
              },
              "X-Chaos-Zip-Note": {
                "description": "Plain-text explanation of the flaw under test.",
                "schema": {
                  "type": "string"
                }
              },
              "X-Chaos-Zip-Claimed-Size": {
                "description": "Present only in `zip64-lie` mode.",
                "schema": {
                  "type": "string"
                }
              },
              "X-Chaos-Zip-Actual-Size": {
                "description": "Present only in `zip64-lie` mode.",
                "schema": {
                  "type": "string"
                }
              },
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-transform"
                }
              }
            },
            "content": {
              "application/zip": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          }
        }
      }
    },
    "/ooxml": {
      "get": {
        "operationId": "ooxmlChaos",
        "externalDocs": {
          "description": "Endpoint reference on catastrophic.io",
          "url": "https://catastrophic.io/chaos/endpoints/format/ooxml/"
        },
        "tags": [
          "payload"
        ],
        "summary": "Office Open XML (DOCX/XLSX/PPTX) packages with one structural lie",
        "description": "Returns a DOCX, XLSX, or PPTX container where one structural part of the OOXML package disagrees with the rest. Targets document-processing pipelines, e-discovery, document-conversion services, and OOXML parsers in general. Reuses the STORED ZIP framing from `/zip-chaos` — the container itself is always a valid ZIP. `Cache-Control: no-transform` is set so the Cloudflare edge does not rewrite the body. `X-Chaos-Ooxml-Note` explains the flaw in plain text. Macro-enabled containers, encrypted OOXML, and embedded OLE objects are deliberately not served.\n",
        "parameters": [
          {
            "name": "format",
            "in": "query",
            "description": "`docx` (default) — Word document.\n`xlsx` — Excel workbook.\n`pptx` — PowerPoint presentation. Includes the full slide-master / slide-layout / theme triangle required for conformant readers.\n",
            "schema": {
              "type": "string",
              "enum": [
                "docx",
                "xlsx",
                "pptx"
              ],
              "default": "docx"
            }
          },
          {
            "name": "mode",
            "in": "query",
            "description": "`wrong-content-type` (default) — `[Content_Types].xml` declares the main part with another OOXML format's content type. Type-trusting parsers fail; content-sniffing parsers succeed.\n`missing-part` — root `.rels` references the main document part; the ZIP has the part at a typo'd path. Conformant readers report corruption.\n`dangling-relationship` — main part's `.rels` declares an image relationship to `media/image1.png` that isn't in the package. Renderers vary (placeholder vs hard fail).\n`format-confusion` — `[Content_Types].xml` declares main parts for all three OOXML formats; only the current format's tree is present.\n",
            "schema": {
              "type": "string",
              "enum": [
                "wrong-content-type",
                "missing-part",
                "dangling-relationship",
                "format-confusion"
              ],
              "default": "wrong-content-type"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OOXML package whose structure contains one deliberate lie. Body Content-Type varies by `format`.",
            "headers": {
              "X-Chaos-Ooxml-Format": {
                "schema": {
                  "type": "string"
                }
              },
              "X-Chaos-Ooxml-Mode": {
                "schema": {
                  "type": "string"
                }
              },
              "X-Chaos-Ooxml-Note": {
                "description": "Plain-text explanation of the flaw under test.",
                "schema": {
                  "type": "string"
                }
              },
              "Content-Disposition": {
                "schema": {
                  "type": "string"
                }
              },
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-transform"
                }
              }
            },
            "content": {
              "application/vnd.openxmlformats-officedocument.wordprocessingml.document": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "application/vnd.openxmlformats-officedocument.presentationml.presentation": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          }
        }
      }
    },
    "/pdf": {
      "get": {
        "operationId": "pdfChaos",
        "externalDocs": {
          "description": "Endpoint reference on catastrophic.io",
          "url": "https://catastrophic.io/chaos/endpoints/format/pdf/"
        },
        "tags": [
          "payload"
        ],
        "summary": "PDF documents with one deliberate structural lie",
        "description": "Returns a PDF where one element of the object graph deliberately lies. Targets document-ingestion pipelines, security scanners, and PDF parsers. Built at request time using traditional cross-reference table format (PDF 1.4) — all chaos lives in text fields. `Cache-Control: no-transform` prevents the Cloudflare edge from rewriting the body. `X-Chaos-Pdf-Note` explains the flaw in plain text. Recursive page trees, working-exploit JavaScript, and font/glyph corruption are deliberately not served.\n",
        "parameters": [
          {
            "name": "mode",
            "in": "query",
            "description": "`bad-xref` (default) — xref byte offsets shifted +100 bytes; object lookup by offset fails. Strict readers fail to resolve any object; lenient readers (most browsers, modern Acrobat) may recover by scanning for `obj` markers.\n`page-count-lie` — /Count declares 100 pages; only one page object exists in the file.\n`encrypted-ghost` — trailer /Encrypt references object 9 which is not defined anywhere in the file.\n`javascript-action` — Catalog OpenAction executes app.alert(\"pdf-chaos\") on open; JS-enabled readers pop an alert, JS-disabled readers open silently, AV scanners may flag any JS presence.\n",
            "schema": {
              "type": "string",
              "enum": [
                "bad-xref",
                "page-count-lie",
                "encrypted-ghost",
                "javascript-action"
              ],
              "default": "bad-xref"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "PDF-shaped body whose structure contains one deliberate lie.",
            "headers": {
              "X-Chaos-Pdf-Mode": {
                "schema": {
                  "type": "string"
                }
              },
              "X-Chaos-Pdf-Note": {
                "description": "Plain-text explanation of the flaw under test.",
                "schema": {
                  "type": "string"
                }
              },
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-transform"
                }
              }
            },
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          }
        }
      }
    },
    "/html": {
      "get": {
        "operationId": "htmlChaos",
        "externalDocs": {
          "description": "Endpoint reference on catastrophic.io",
          "url": "https://catastrophic.io/chaos/endpoints/format/html/"
        },
        "tags": [
          "payload"
        ],
        "summary": "HTML documents with deliberate structural flaws",
        "description": "Returns an HTML document with a deliberate structural flaw. Useful for browser-parser testing, scraper robustness, and ML preprocessing of HTML. Default mode produces visible misnesting on a bare GET. `Cache-Control: no-transform` is set so the Cloudflare edge does not sanitise or re-encode the broken markup on the way out. The `X-Chaos-Html-Note` header explains the flaw in plain text.\n",
        "parameters": [
          {
            "name": "mode",
            "in": "query",
            "description": "`mismatched-tags` (default) — `<div><span></div></span>`; HTML5 parsers auto-correct, strict XML parsers reject.\n`meta-charset-conflict` — declares `<meta charset=\"utf-8\">` but body bytes are CP1252 (smart quotes, en/em-dash, euro, ellipsis); utf-8 parsers replace or fail.\n`doctype-mismatch` — XHTML 1.0 Strict doctype with HTML5 syntax (unclosed `<br>` etc.).\n`script-without-end` — `<script>` opens and never closes; browsers swallow the rest of the document into the script body.\n",
            "schema": {
              "type": "string",
              "enum": [
                "mismatched-tags",
                "meta-charset-conflict",
                "doctype-mismatch",
                "script-without-end"
              ],
              "default": "mismatched-tags"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "HTML body with the chosen structural flaw.",
            "headers": {
              "X-Chaos-Html-Mode": {
                "schema": {
                  "type": "string"
                }
              },
              "X-Chaos-Html-Note": {
                "description": "Plain-text explanation of the flaw under test.",
                "schema": {
                  "type": "string"
                }
              },
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-transform"
                }
              }
            },
            "content": {
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/problem-details": {
      "get": {
        "operationId": "problemDetailsChaos",
        "externalDocs": {
          "description": "Endpoint reference on catastrophic.io",
          "url": "https://catastrophic.io/chaos/endpoints/format/problem-details/"
        },
        "tags": [
          "payload"
        ],
        "summary": "RFC 9457 Problem Details objects with spec violations",
        "description": "Returns an RFC 9457 Problem Details envelope with one spec-level violation. Targets error-handling code in REST clients, SDKs, and API gateways that parse or dispatch on Problem Details fields. Default mode omits the required `type` field; clients that branch on `type` receive undefined. `X-Chaos-Problem-Details-Note` explains the violation in plain text.\n",
        "parameters": [
          {
            "name": "mode",
            "in": "query",
            "description": "`drop-required` (default) — `type` field omitted; RFC 9457 §3 requires it; clients that branch on type for error routing receive undefined.\n`status-type-shift` — `status` is the string \"404\" instead of integer 404; typed SDKs that deserialize into an int field error.\n`instance-malformed` — `instance` is the bare word \"not-a-uri\"; clients that dereference it get a URL parse error.\n`content-type-lie` — body is valid Problem Details JSON but Content-Type is application/json; clients that dispatch on the media type miss the error envelope.\n",
            "schema": {
              "type": "string",
              "enum": [
                "drop-required",
                "status-type-shift",
                "instance-malformed",
                "content-type-lie"
              ],
              "default": "drop-required"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Problem Details envelope with the chosen spec violation.",
            "headers": {
              "X-Chaos-Problem-Details-Mode": {
                "schema": {
                  "type": "string"
                }
              },
              "X-Chaos-Problem-Details-Note": {
                "description": "Plain-text explanation of the violation under test.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/jsonrpc": {
      "get": {
        "operationId": "jsonRpcChaos",
        "externalDocs": {
          "description": "Endpoint reference on catastrophic.io",
          "url": "https://catastrophic.io/chaos/endpoints/format/jsonrpc/"
        },
        "tags": [
          "payload"
        ],
        "summary": "JSON-RPC 2.0 responses with spec-level violations",
        "description": "Returns a JSON-RPC 2.0 response with one spec-level violation. Used by MCP, Ethereum clients, language servers, and many internal RPC systems — all of which parse the envelope strictly. Default mode omits the required `jsonrpc` version field; strict clients reject or fall back to 1.x parsing rules.\n",
        "parameters": [
          {
            "name": "mode",
            "in": "query",
            "description": "`version-missing` (default) — `jsonrpc` field absent; signals a 1.x response with a different envelope; strict clients reject.\n`id-mismatch` — response `id` is 99 but the request id was 1; clients correlating by id fail to match.\n`result-and-error` — both `result` and `error` present; JSON-RPC 2.0 §5 says they are mutually exclusive.\n`error-code-invalid` — `error.code` is -32001, inside the reserved range but not a standard code.\n",
            "schema": {
              "type": "string",
              "enum": [
                "version-missing",
                "id-mismatch",
                "result-and-error",
                "error-code-invalid"
              ],
              "default": "version-missing"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "JSON-RPC 2.0 envelope with the chosen spec violation.",
            "headers": {
              "X-Chaos-Jsonrpc-Mode": {
                "schema": {
                  "type": "string"
                }
              },
              "X-Chaos-Jsonrpc-Note": {
                "description": "Plain-text explanation of the violation under test.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/jsonapi": {
      "get": {
        "operationId": "jsonApiChaos",
        "externalDocs": {
          "description": "Endpoint reference on catastrophic.io",
          "url": "https://catastrophic.io/chaos/endpoints/format/jsonapi/"
        },
        "tags": [
          "payload"
        ],
        "summary": "JSON:API responses with spec-level violations",
        "description": "Returns a JSON:API response served as `application/vnd.api+json` with one spec-level violation. Targets JSON:API client libraries and serializers. Default mode returns `data` as an array when a single resource is implied; clients that call `response.data.attributes` receive undefined.\n",
        "parameters": [
          {
            "name": "mode",
            "in": "query",
            "description": "`data-shape-confusion` (default) — `data` is an array when a single resource is implied; clients that call response.data.attributes receive undefined.\n`included-orphan` — `included` contains a resource not referenced by any relationship in `data`; JSON:API §5 requires all included resources to be linked.\n`missing-id` — resource object has no `id`; JSON:API §3.1 requires it for server-assigned IDs.\n`type-missing` — resource object has no `type`; JSON:API §3.1 requires it; clients that dispatch on type throw or fall through.\n",
            "schema": {
              "type": "string",
              "enum": [
                "data-shape-confusion",
                "included-orphan",
                "missing-id",
                "type-missing"
              ],
              "default": "data-shape-confusion"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "JSON:API envelope with the chosen spec violation.",
            "headers": {
              "X-Chaos-Jsonapi-Mode": {
                "schema": {
                  "type": "string"
                }
              },
              "X-Chaos-Jsonapi-Note": {
                "description": "Plain-text explanation of the violation under test.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/vnd.api+json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/oauth-token": {
      "get": {
        "operationId": "oauthTokenChaos",
        "externalDocs": {
          "description": "Endpoint reference on catastrophic.io",
          "url": "https://catastrophic.io/chaos/endpoints/format/oauth-token/"
        },
        "tags": [
          "payload"
        ],
        "summary": "RFC 6749 token responses with field-level violations",
        "description": "Returns an OAuth 2.0 token response per RFC 6749 §5.1 with one field-level violation. OAuth SDKs and libraries consume these responses and often have weak type enforcement. Default mode returns `expires_in` as the string \"3600\" instead of integer 3600; typed SDKs error and expiry arithmetic produces NaN. `Cache-Control: no-store` matches real token endpoint behaviour.\n",
        "parameters": [
          {
            "name": "mode",
            "in": "query",
            "description": "`expires-in-type-shift` (default) — `expires_in` is the string \"3600\" instead of integer 3600; RFC 6749 §5.1 requires a number.\n`scope-delimiter-wrong` — `scope` uses comma separators instead of the space separators required by RFC 6749 §3.3.\n`token-type-nonstandard` — `token_type` is \"token\" instead of \"Bearer\"; clients that exact-match fail to attach Authorization headers.\n`id-token-without-scope` — `id_token` present but `scope` omits \"openid\"; OIDC Core §3.1.3.3 requires the openid scope.\n",
            "schema": {
              "type": "string",
              "enum": [
                "expires-in-type-shift",
                "scope-delimiter-wrong",
                "token-type-nonstandard",
                "id-token-without-scope"
              ],
              "default": "expires-in-type-shift"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OAuth token response with the chosen field-level violation.",
            "headers": {
              "X-Chaos-Oauth-Token-Mode": {
                "schema": {
                  "type": "string"
                }
              },
              "X-Chaos-Oauth-Token-Note": {
                "description": "Plain-text explanation of the violation under test.",
                "schema": {
                  "type": "string"
                }
              },
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/cloudevents": {
      "get": {
        "operationId": "cloudEventsChaos",
        "externalDocs": {
          "description": "Endpoint reference on catastrophic.io",
          "url": "https://catastrophic.io/chaos/endpoints/format/cloudevents/"
        },
        "tags": [
          "payload"
        ],
        "summary": "CloudEvents 1.0 envelopes with spec violations",
        "description": "Returns a CloudEvents 1.0 envelope served as `application/cloudevents+json` with one spec-level violation. Used by serverless platforms, event grids, and message buses — all of which validate the envelope before dispatching to handlers. Default mode claims `specversion` \"0.3\" instead of \"1.0\"; consumers that validate version before dispatching reject or apply 0.3 parsing rules.\n",
        "parameters": [
          {
            "name": "mode",
            "in": "query",
            "description": "`specversion-lie` (default) — `specversion` is \"0.3\" instead of \"1.0\"; consumers that validate version before dispatching reject the envelope.\n`type-as-number` — `type` is integer 42; CloudEvents §3.1 requires a non-empty string; consumers that substring-match on type throw a type error.\n`datacontenttype-mismatch` — `datacontenttype` says application/json but `data` is a bare string; consumers call JSON.parse on non-JSON and throw.\n`data-base64-confusion` — both `data` and `data_base64` present; CloudEvents §3.1 says they are mutually exclusive.\n",
            "schema": {
              "type": "string",
              "enum": [
                "specversion-lie",
                "type-as-number",
                "datacontenttype-mismatch",
                "data-base64-confusion"
              ],
              "default": "specversion-lie"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "CloudEvents 1.0 envelope with the chosen spec violation.",
            "headers": {
              "X-Chaos-Cloudevents-Mode": {
                "schema": {
                  "type": "string"
                }
              },
              "X-Chaos-Cloudevents-Note": {
                "description": "Plain-text explanation of the violation under test.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/cloudevents+json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/geojson": {
      "get": {
        "operationId": "geoJsonChaos",
        "externalDocs": {
          "description": "Endpoint reference on catastrophic.io",
          "url": "https://catastrophic.io/chaos/endpoints/format/geojson/"
        },
        "tags": [
          "payload"
        ],
        "summary": "GeoJSON documents with RFC 7946 violations",
        "description": "Returns a GeoJSON Feature served as `application/geo+json` with one RFC 7946 violation. Targets GIS clients, mapping libraries, and spatial data pipelines. Default mode returns a Polygon whose ring does not close; strict validators reject, lenient renderers auto-close and silently accept the server-side bug.\n",
        "parameters": [
          {
            "name": "mode",
            "in": "query",
            "description": "`polygon-not-closed` (default) — Polygon ring last position differs from first; RFC 7946 §3.1.6 requires an identical closing position.\n`coordinates-swapped` — coordinates are in [latitude, longitude] order instead of GeoJSON's required [longitude, latitude]; geometry is syntactically valid so parsers accept it and place points in the wrong hemisphere.\n`type-coords-mismatch` — `type` is \"Point\" but `coordinates` is an array of rings; strict validators reject, lenient renderers produce NaN coordinates.\n`properties-missing` — `properties` member absent from the Feature; RFC 7946 §3.2 requires it even if null.\n",
            "schema": {
              "type": "string",
              "enum": [
                "polygon-not-closed",
                "coordinates-swapped",
                "type-coords-mismatch",
                "properties-missing"
              ],
              "default": "polygon-not-closed"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "GeoJSON Feature with the chosen RFC 7946 violation.",
            "headers": {
              "X-Chaos-Geojson-Mode": {
                "schema": {
                  "type": "string"
                }
              },
              "X-Chaos-Geojson-Note": {
                "description": "Plain-text explanation of the violation under test.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/geo+json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/schema-org": {
      "get": {
        "operationId": "schemaOrgChaos",
        "externalDocs": {
          "description": "Endpoint reference on catastrophic.io",
          "url": "https://catastrophic.io/chaos/endpoints/format/schema-org/"
        },
        "tags": [
          "payload"
        ],
        "summary": "Schema.org / JSON-LD documents with vocabulary and context violations",
        "description": "Returns a JSON-LD document served as `application/ld+json` with one Schema.org spec violation. Targets structured-data extractors, rich-result generators, and JSON-LD processors that crawl or ingest semantic markup. Default mode points `@context` at a non-existent IRI so processors that dereference get a 404 and those that cache by IRI fail to match the vocabulary.\n",
        "parameters": [
          {
            "name": "mode",
            "in": "query",
            "description": "`context-lie` (default) — `@context` is \"https://schema.example.invalid\"; JSON-LD processors that dereference the context get a 404.\n`type-unknown` — `@type` is \"ChaosEntity\", not in any Schema.org vocabulary; rich-result renderers reject.\n`missing-required` — Recipe without `name` or `recipeIngredient`; search crawlers silently drop the rich result.\n`context-array-conflict` — second `@context` entry remaps \"name\" to a custom IRI, shadowing schema.org's definition.\n",
            "schema": {
              "type": "string",
              "enum": [
                "context-lie",
                "type-unknown",
                "missing-required",
                "context-array-conflict"
              ],
              "default": "context-lie"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "JSON-LD document with the chosen Schema.org violation.",
            "headers": {
              "X-Chaos-Schema-Org-Mode": {
                "schema": {
                  "type": "string"
                }
              },
              "X-Chaos-Schema-Org-Note": {
                "description": "Plain-text explanation of the violation under test.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/ld+json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/jwt-payload": {
      "get": {
        "operationId": "jwtPayloadChaos",
        "externalDocs": {
          "description": "Endpoint reference on catastrophic.io",
          "url": "https://catastrophic.io/chaos/endpoints/format/jwt-payload/"
        },
        "tags": [
          "payload"
        ],
        "summary": "JWT payload (claims) with RFC 7519 violations",
        "description": "Returns a JSON object representing a JWT claim set with one RFC 7519 violation. Distinct from `/jwt-chaos` (token-validation flaws) and `/auth-chaos` (challenge framing) — this tests the claim set that libraries deserialize after signature verification. Default mode sends `exp` as a string instead of a NumericDate integer.\n",
        "parameters": [
          {
            "name": "mode",
            "in": "query",
            "description": "`exp-as-string` (default) — `exp` is a string instead of a NumericDate integer per RFC 7519 §4.1.4.\n`iss-malformed` — `iss` is a bare word, not a URI; validators that require URI format reject.\n`aud-mixed-types` — `aud` array contains string, integer, and boolean; RFC 7519 §4.1.3 requires StringOrURI.\n`reserved-claim-collision` — `sub` is an object instead of a string principal identifier.\n",
            "schema": {
              "type": "string",
              "enum": [
                "exp-as-string",
                "iss-malformed",
                "aud-mixed-types",
                "reserved-claim-collision"
              ],
              "default": "exp-as-string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "JWT claim set with the chosen RFC 7519 violation.",
            "headers": {
              "X-Chaos-Jwt-Payload-Mode": {
                "schema": {
                  "type": "string"
                }
              },
              "X-Chaos-Jwt-Payload-Note": {
                "description": "Plain-text explanation of the violation under test.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/activitystreams": {
      "get": {
        "operationId": "activityStreamsChaos",
        "externalDocs": {
          "description": "Endpoint reference on catastrophic.io",
          "url": "https://catastrophic.io/chaos/endpoints/format/activitystreams/"
        },
        "tags": [
          "payload"
        ],
        "summary": "ActivityStreams 2.0 / ActivityPub objects with spec violations",
        "description": "Returns an ActivityStreams 2.0 object served as `application/activity+json` with one ActivityPub spec violation. Consumed by Mastodon, Misskey, and other Fediverse servers during federation. Default mode omits `@context`; servers that check for it reject the activity or fail to expand JSON-LD terms.\n",
        "parameters": [
          {
            "name": "mode",
            "in": "query",
            "description": "`context-missing` (default) — `@context` absent; ActivityPub §3.1 requires it on every networked object.\n`type-not-vocabulary` — `type` is \"ChaosPost\", not in the AS2 vocabulary.\n`actor-not-uri` — `actor` is a bare string; ActivityPub §4.1 requires a dereferenceable URI for key verification.\n`object-shape-variance` — Like activity with `object` as a bare string instead of a URI or embedded object.\n",
            "schema": {
              "type": "string",
              "enum": [
                "context-missing",
                "type-not-vocabulary",
                "actor-not-uri",
                "object-shape-variance"
              ],
              "default": "context-missing"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "ActivityStreams 2.0 object with the chosen spec violation.",
            "headers": {
              "X-Chaos-Activitystreams-Mode": {
                "schema": {
                  "type": "string"
                }
              },
              "X-Chaos-Activitystreams-Note": {
                "description": "Plain-text explanation of the violation under test.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/activity+json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/web-annotation": {
      "get": {
        "operationId": "webAnnotationChaos",
        "externalDocs": {
          "description": "Endpoint reference on catastrophic.io",
          "url": "https://catastrophic.io/chaos/endpoints/format/web-annotation/"
        },
        "tags": [
          "payload"
        ],
        "summary": "W3C Web Annotation objects with spec violations",
        "description": "Returns a W3C Web Annotation object served as `application/ld+json` (with `anno.jsonld` profile) with one spec violation. Targets annotation tools, highlighting libraries, and knowledge-management systems that store and replay annotations. Default mode omits the required `id` field.\n",
        "parameters": [
          {
            "name": "mode",
            "in": "query",
            "description": "`id-missing` (default) — `id` field absent; W3C Web Annotation §3.1 requires it for persistence and deduplication.\n`body-as-string` — `body` is a plain string instead of a TextualBody object or URI.\n`motivation-invalid` — `motivation` is \"chaos\", not in the W3C motivation vocabulary.\n`target-shape-variance` — `target` is a Specific Resource object instead of a URI string.\n",
            "schema": {
              "type": "string",
              "enum": [
                "id-missing",
                "body-as-string",
                "motivation-invalid",
                "target-shape-variance"
              ],
              "default": "id-missing"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Web Annotation with the chosen W3C spec violation.",
            "headers": {
              "X-Chaos-Web-Annotation-Mode": {
                "schema": {
                  "type": "string"
                }
              },
              "X-Chaos-Web-Annotation-Note": {
                "description": "Plain-text explanation of the violation under test.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/ld+json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/sse": {
      "get": {
        "operationId": "sseChaos",
        "externalDocs": {
          "description": "Endpoint reference on catastrophic.io",
          "url": "https://catastrophic.io/chaos/endpoints/format/sse/"
        },
        "tags": [
          "payload"
        ],
        "summary": "Server-Sent Events with WHATWG EventSource violations",
        "description": "Returns a `text/event-stream` response with one WHATWG EventSource spec violation. Default mode sends payload lines with no `data:` prefix, so conformant clients deliver empty event.data to their handlers and the stream looks silent.\n",
        "parameters": [
          {
            "name": "mode",
            "in": "query",
            "description": "`missing-data-prefix` (default) — payload lines have no `data:` prefix; conformant clients ignore unrecognized field names.\n`event-without-listener` — all events use named `event:` fields; `EventSource.onmessage` never fires.\n`id-discontinuity` — event ids go backwards; Last-Event-ID reconnect gets an unpredictable cursor.\n`crlf-mix` — line endings alternate CRLF and LF; parsers that normalize only one style misparse field boundaries.\n",
            "schema": {
              "type": "string",
              "enum": [
                "missing-data-prefix",
                "event-without-listener",
                "id-discontinuity",
                "crlf-mix"
              ],
              "default": "missing-data-prefix"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "SSE stream with the chosen violation.",
            "headers": {
              "X-Chaos-Sse-Mode": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "text/event-stream": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/multipart": {
      "get": {
        "operationId": "multipartChaos",
        "externalDocs": {
          "description": "Endpoint reference on catastrophic.io",
          "url": "https://catastrophic.io/chaos/endpoints/format/multipart/"
        },
        "tags": [
          "payload"
        ],
        "summary": "multipart/form-data with RFC 7578 violations",
        "description": "Returns a `multipart/form-data` response with one RFC 7578 spec violation. Default mode declares one boundary in the Content-Type and uses a different one in the body, so strict parsers find no opening delimiter.\n",
        "parameters": [
          {
            "name": "mode",
            "in": "query",
            "description": "`boundary-mismatch` (default) — Content-Type boundary does not match body delimiter.\n`trailing-crlf-confusion` — closing boundary `--xxx--` has no trailing CRLF; strict parsers treat the stream as unterminated.\n`disposition-name-injection` — two `name=` parameters on one Content-Disposition; RFC 7578 leaves precedence undefined.\n`nested-multipart` — inner part uses Content-Type: multipart/mixed; most form-data parsers do not recurse.\n",
            "schema": {
              "type": "string",
              "enum": [
                "boundary-mismatch",
                "trailing-crlf-confusion",
                "disposition-name-injection",
                "nested-multipart"
              ],
              "default": "boundary-mismatch"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "multipart/form-data response with the chosen violation.",
            "headers": {
              "X-Chaos-Multipart-Mode": {
                "schema": {
                  "type": "string"
                }
              },
              "X-Chaos-Multipart-Note": {
                "description": "Plain-text explanation of the violation under test.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "multipart/form-data": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/toml": {
      "get": {
        "operationId": "tomlChaos",
        "externalDocs": {
          "description": "Endpoint reference on catastrophic.io",
          "url": "https://catastrophic.io/chaos/endpoints/format/toml/"
        },
        "tags": [
          "payload"
        ],
        "summary": "TOML documents with TOML 1.0 violations",
        "description": "Returns an `application/toml` document with one TOML 1.0 violation or parser-ambiguity pattern. Default mode mixes int, string, and float in a single array; many parsers accept silently and downstream schema validators fail.\n",
        "parameters": [
          {
            "name": "mode",
            "in": "query",
            "description": "`mixed-array-types` (default) — array mixes int, string, and float; TOML 1.0 forbids mixed types in arrays.\n`unquoted-datetime` — datetime value is unquoted and not in RFC 3339 format.\n`duplicate-table` — the same table is defined twice.\n`invalid-nesting` — a table is defined after it has been used as a simple key.\n`datetime-offset-lie` — offset `+99:00` is structurally valid syntax but semantically impossible.\n`dotted-key-table-collision` — table is defined, then a dotted key redefines it; TOML 1.0 forbids this.\n`trailing-comma-inline-table` — inline table has a trailing comma; TOML 1.0 forbids this (unlike arrays).\n",
            "schema": {
              "type": "string",
              "enum": [
                "mixed-array-types",
                "unquoted-datetime",
                "duplicate-table",
                "invalid-nesting",
                "datetime-offset-lie",
                "dotted-key-table-collision",
                "trailing-comma-inline-table"
              ],
              "default": "mixed-array-types"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "TOML document with the chosen violation.",
            "headers": {
              "X-Chaos-Toml-Mode": {
                "schema": {
                  "type": "string"
                }
              },
              "X-Chaos-Toml-Note": {
                "description": "Plain-text explanation of the violation under test.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/toml": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/yaml": {
      "get": {
        "operationId": "yamlChaos",
        "externalDocs": {
          "description": "Endpoint reference on catastrophic.io",
          "url": "https://catastrophic.io/chaos/endpoints/format/yaml/"
        },
        "tags": [
          "payload"
        ],
        "summary": "YAML documents with YAML 1.1 vs 1.2 ambiguity and parser adversity",
        "description": "Returns an `application/yaml` document exploiting the YAML 1.1 vs 1.2 split and parser ambiguity. Default sends the Norway problem: bare `NO/YES/ON/OFF` scalars that YAML 1.1 parsers coerce to booleans while YAML 1.2 parsers leave as strings.\n",
        "parameters": [
          {
            "name": "mode",
            "in": "query",
            "description": "`norway-problem` (default) — bare `NO/YES/ON/OFF` scalars; YAML 1.1 vs 1.2 parsers disagree on the type.\n`anchor-cycle` — recursive anchor reference; parsers without cycle detection exhaust stack or heap.\n`duplicate-keys` — a key appears twice; spec behavior is application-defined.\n`tag-lie` — explicit `!!int` tag applied to a non-integer string.\n",
            "schema": {
              "type": "string",
              "enum": [
                "norway-problem",
                "anchor-cycle",
                "duplicate-keys",
                "tag-lie"
              ],
              "default": "norway-problem"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "YAML document with the chosen violation.",
            "headers": {
              "X-Chaos-Yaml-Mode": {
                "schema": {
                  "type": "string"
                }
              },
              "X-Chaos-Yaml-Note": {
                "description": "Plain-text explanation of the violation under test.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/yaml": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/json-feed": {
      "get": {
        "operationId": "jsonFeedChaos",
        "externalDocs": {
          "description": "Endpoint reference on catastrophic.io",
          "url": "https://catastrophic.io/chaos/endpoints/format/json-feed/"
        },
        "tags": [
          "payload"
        ],
        "summary": "JSON Feed documents with semantic schema violations",
        "description": "Returns a JSON Feed 1.1 document served as `application/feed+json` with one semantic schema violation that parses as JSON but breaks the spec. Targets feed readers and aggregators. Default mode sends `version` as the bare string \"1.1\" instead of the required full URL \"https://jsonfeed.org/version/1.1\".\n",
        "parameters": [
          {
            "name": "mode",
            "in": "query",
            "description": "`version-mismatch` (default) — `version` is \"1.1\" instead of the full URL \"https://jsonfeed.org/version/1.1\" per JSON Feed §2.\n`items-id-not-unique` — two items share the same id; aggregators that dedupe on id collapse both entries.\n`item-url-malformed` — an item's url is a relative path; JSON Feed §4.1.2 requires absolute URLs.\n`feed-url-wrong` — feed_url points to a different host; subscription dedup and refresh routing target the wrong place.\n",
            "schema": {
              "type": "string",
              "enum": [
                "version-mismatch",
                "items-id-not-unique",
                "item-url-malformed",
                "feed-url-wrong"
              ],
              "default": "version-mismatch"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "JSON Feed document with the chosen spec violation.",
            "headers": {
              "X-Chaos-Json-Feed-Mode": {
                "schema": {
                  "type": "string"
                }
              },
              "X-Chaos-Json-Feed-Note": {
                "description": "Plain-text explanation of the violation under test.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/feed+json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "responses": {
      "EchoResponse": {
        "description": "The reflected request.",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "endpoint": {
                  "type": "string",
                  "const": "/echo"
                },
                "method": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "path": {
                  "type": "string"
                },
                "query": {
                  "type": "object",
                  "additionalProperties": {
                    "type": "string"
                  }
                },
                "headers": {
                  "type": "object",
                  "additionalProperties": {
                    "type": "string"
                  }
                },
                "body": {
                  "description": "Parsed JSON if the body was valid JSON, otherwise the raw string, otherwise null.",
                  "oneOf": [
                    {
                      "type": "object"
                    },
                    {
                      "type": "array"
                    },
                    {
                      "type": "string"
                    },
                    {
                      "type": "null"
                    }
                  ]
                }
              }
            }
          }
        }
      }
    }
  }
}
