# Web Advanced: support matrix Every capability is off unless named. `WebAdvancedPromotionGate.forFeature` is the machine-checked form of the last two columns; if this table disagrees with it, the code wins. | Capability | Flag | What it adds | Required suites | Soak | | --- | --- | --- | --- | --- | | `MVC_VIRTUAL_THREADS` | `backend.web.advanced.mvc-virtual-threads.enabled` | A different scheduling model for **every** request | `web:test`, `webCrossStackParityTest`, `virtual-thread-admission`, `pinning-jfr` | 24h | | `WEBFLUX_BLOCKING_BRIDGE` | `…webflux-blocking-bridge.enabled` | A bounded offload; affects the shared event loop | `web:test`, `blocking-bridge-bounded`, `event-loop-guard` | 24h | | `JSON_MERGE_PATCH` | `…json-merge-patch.enabled` | RFC 7396, partial documents | `web:test`, `patch-security`, `patch-atomicity` | 8h | | `JSON_PATCH` | `…json-patch.enabled` | RFC 6902, arbitrary pointers | as merge patch | 8h | | `SSE` | `…sse.enabled` | Long-lived connections | `web:test`, `streaming-soak-10k`, `slow-consumer-bounded`, `cancellation-propagation`, `pod-drain` | 24h | | `NDJSON` | `…ndjson.enabled` | Long-lived connections | as SSE | 24h | | `JSON_SEQUENCE` | `…json-sequence.enabled` | Long-lived connections | as SSE | 24h | | `FUNCTIONAL_WEBFLUX` | `…functional-webflux.enabled` | Routes with no annotation to scan | `web:test`, `functional-route-parity` | 8h | | `CBOR` | `…cbor.enabled` | A second decoder | `web:test`, `codec-security`, `codec-budget` | 8h | | `XML` | `…xml.enabled` | A decoder with dangerous defaults | as CBOR | 8h | | `OPENAPI_32` | `…openapi-32.enabled` | A parallel description artifact | `web:test`, `openapi-32-toolchain-matrix` | 8h | | `RATELIMIT_DRAFT_HEADERS` | `…ratelimit-draft-headers.enabled` | Additive response headers | `web:test`, `ratelimit-draft-headers` | 8h | ## Two capabilities change requests that do not use them `WebAdvancedFeature.affectsUnrelatedRequests()` is true for exactly `MVC_VIRTUAL_THREADS` and `WEBFLUX_BLOCKING_BRIDGE`. A codec affects only requests that negotiate it; a virtual-thread executor affects every request in the process, and the blocking bridge affects the event loop they all share. `WebAdvancedFeatureFlags.stableBehaviourPreserved()` reports whether either is on. That distinction is why those two soak for 24 hours and why their rollback test is the one that matters most. ## What is refused, and why the row is here | Refused | Reason | | --- | --- | | Virtual threads without an admission limit | The pool size *was* the admission policy. Removing it accepts every arrival and queues them on downstream budgets that did not grow. | | A blocking offload for an unregistered operation | `boundedElastic()` is reachable from anywhere and unbounded in practice; an unenumerable set of offloads is invisible until the pool is the heap. | | A merge patch outside its field allowlist | A merge patch is partial, so no DTO's absent fields say "not permitted". Without an allowlist the writable set grows every time somebody adds a field. | | A JSON Patch pointer above its allowed prefix | Replacing a parent deletes every sibling, so permission on a child cannot grant it. | | A `move` whose source is unauthorized | Checking only the destination lets a caller relocate data out of a field they may not touch. | | XML with a DTD or an external entity | Billion-laughs and XXE. Neither errors when it fires; the parse succeeds and the document contains something it should not. | | A non-JSON representation without all three gates | Flag, route `produces`, and client allowlist. An `Accept` header is not evidence the route was tested against that codec. | | Compression-style silent fallback on a 406 | A client that asked only for CBOR and gets JSON parses the bytes as CBOR and fails somewhere far away. | | Changing HTTP status after commit | The body becomes half stream, half JSON, and the 200 is cached. | | Unbounded buffering for a slow consumer | It moves the client's slowness into the server's heap. | ## Promotion Each capability is promoted on its own evidence. Two conditions apply to all and are not waivable: - **Rollback exercised.** A flag nobody has turned off is not known to turn off. - **Stable behaviour unchanged with the feature off.** If it is not, the feature was never optional and every deployment has it. Parsers and patch appliers additionally require a security review — `WebAdvancedPromotionGate.needsSecurityReview` names them. See `docs/adr/ADR-WEB-ADV-001-streaming-is-live-delivery.md`, `ADR-WEB-ADV-002-virtual-threads-do-not-remove-admission.md`, and `ADR-WEB-ADV-003-openapi-32-remains-experimental.md`.