Skip to content

chore: release 3.19.0 - #13976

Open
shreemaan-abhishek wants to merge 4 commits into
masterfrom
release/3.19
Open

shreemaan-abhishek wants to merge 4 commits into
masterfrom
release/3.19

Conversation

@shreemaan-abhishek

Copy link
Copy Markdown
Contributor

Description

Release 3.19.0.

This PR:

  • Adds the 3.19.0 section to CHANGELOG.md (and its Table of Contents entry), covering all PRs merged since the release/3.18 branch was cut. Backward-incompatible changes are grouped under Change and marked with ⚠️.
  • Bumps the version to 3.19.0 in apisix/core/version.lua and docs/{en,zh}/latest/config.json.
  • Extends ci/check_changelog_prs.ts IGNORE_PRS with changes that are not user-facing: CI-only work whose fix(ci) subject prefix dodges the docs/chore/test/ci type filter (fix(ci): unbreak CI under pnpm 12 #13921, fix(ci): stop rerun_flaky_tests from turning unparsable failures into a pass #13923).
  • Bumps APISIX_DASHBOARD_COMMIT in .requirements to fa2fd0f (current apisix-dashboard master). APISIX_RUNTIME is already at 1.3.18, so it is unchanged.

Breaking changes (see the Change section)

Only changes that break an existing configuration or stored state on upgrade, with no adjustment made, are listed here.

Checklist

  • I have explained the need for this PR and the problem it solves
  • I have explained the changes or the new features added to this PR
  • I have added tests corresponding to this change (N/A: release bookkeeping only, no runtime code changes)
  • I have updated the documentation to reflect this change
  • I have verified that this change is backward compatible (this PR itself only touches release metadata; the breaking changes it documents landed in their own PRs and are listed in the Change section)

Comment thread CHANGELOG.md
- fix(control): always report healthcheck nodes as a JSON array, so `nodes` and the top-level list of `/v1/healthcheck` are `[]` instead of `{}` when empty [#13891](https://github.com/apache/apisix/pull/13891)
- fix(plugin): align unavailable plugin handling: reject unknown plugin names before persistence, keep data-plane loading tolerant of them, and warn when one is skipped [#13928](https://github.com/apache/apisix/pull/13928)
- fix: preserve servlet upstream URI boundaries by encoding the original path before proxying when servlet-style normalization is enabled [#13914](https://github.com/apache/apisix/pull/13914)
- feat: label WebSocket sessions with `request_type=websocket`. A request answered with `101 Switching Protocols` is reported as `websocket` instead of `traditional_http` in `apisix_http_status`, `apisix_http_latency` and `apisix_bandwidth`, so a session can be kept out of latency queries [#13909](https://github.com/apache/apisix/pull/13909)

@kayx23 kayx23 Sep 21, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be marked as backward-incompatible and moved into Change (grouped with #13915). For existing enable_websocket routes, apisix_http_status, apisix_http_latency, and apisix_bandwidth series for successful 101 responses change request_type from traditional_http to websocket. PromQL selectors, recording rules, dashboards, and alerts that filter on the old value can silently lose WebSocket traffic after upgrade. Please scope the wording to the enable_websocket/NGINX proxy path: the new ws/wss content path does not run http_header_filter_phase, where this label is assigned.

Comment thread CHANGELOG.md
- feat: chaitin-waf response logging through `log_resp`, `resp_body_size` and `extra_ignored_content_types`, reported asynchronously after the response has been handed back to the client [#13763](https://github.com/apache/apisix/pull/13763)
- feat(ai-proxy-multi): let configured HTTP statuses trigger a fallback via `fallback_http_statuses` [#13852](https://github.com/apache/apisix/pull/13852)
- feat(saml-auth): add the lua-resty-saml 0.2.6 validation options `idp_issuers`, `sp_acs_url`, `sp_audiences`, `clock_skew`, `replay_dict` and `replay_ttl` [#13964](https://github.com/apache/apisix/pull/13964)
- fix(redis): send the TLS SNI and add `redis_server_name`, so a Redis behind a name-routed TLS front works with `redis_ssl: true`; the SNI is skipped for an IP literal host [#13938](https://github.com/apache/apisix/pull/13938)

@kayx23 kayx23 Sep 21, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has a backward-incompatible case that should be called out in Change: for the single-node policy: redis path, when redis_ssl_verify: true, the landed code now passes redis_host (or redis_server_name) as server_name, so hostname verification becomes effective. An existing DNS alias not covered by the Redis certificate now fails with a certificate-host mismatch. The migration is to set redis_server_name to the certificate identity or replace the certificate; disabling verification is only an insecure compatibility fallback. This does not describe the Redis Cluster or Sentinel paths, and SNI is skipped only when the configured host/server-name value itself is an IP literal.

Comment thread CHANGELOG.md
- fix(ai-providers): encode the Vertex AI model path segment [#13872](https://github.com/apache/apisix/pull/13872)
- fix(ai-cache): key the passthrough protocol on the client method, path and query, so two upstream endpoints no longer collide on one cache entry [#13887](https://github.com/apache/apisix/pull/13887)
- fix(ai-aliyun-content-moderation): report final results without usage [#13922](https://github.com/apache/apisix/pull/13922)
- fix(feishu-auth, dingtalk-auth): bind the authorization code to the session that started the login. A random `state` is appended to the `redirect_uri` redirect and required back on the callback, so a code obtained elsewhere is no longer accepted on any session. Whatever serves `redirect_uri` has to pass `state` on to the identity provider; the header code path (`X-Feishu-Code` / `X-DingTalk-Code`) is unchanged [#13806](https://github.com/apache/apisix/pull/13806)

@kayx23 kayx23 Sep 21, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be marked backward-incompatible and moved into Change. Existing browser flows that only forward the authorization code now return 401 Invalid state; the application serving redirect_uri must forward the generated state to the IdP and preserve it on the callback. The header-code paths remain compatible.

Comment thread CHANGELOG.md
- fix(ai-cache): key the passthrough protocol on the client method, path and query, so two upstream endpoints no longer collide on one cache entry [#13887](https://github.com/apache/apisix/pull/13887)
- fix(ai-aliyun-content-moderation): report final results without usage [#13922](https://github.com/apache/apisix/pull/13922)
- fix(feishu-auth, dingtalk-auth): bind the authorization code to the session that started the login. A random `state` is appended to the `redirect_uri` redirect and required back on the callback, so a code obtained elsewhere is no longer accepted on any session. Whatever serves `redirect_uri` has to pass `state` on to the identity provider; the header code path (`X-Feishu-Code` / `X-DingTalk-Code`) is unchanged [#13806](https://github.com/apache/apisix/pull/13806)
- fix(jwe-decrypt): accept JWE tokens that authenticate the protected header, as RFC 7516 requires, and reject an unsupported `alg` or `enc` [#13889](https://github.com/apache/apisix/pull/13889)

@kayx23 kayx23 Sep 21, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please split the compatibility-sensitive part into Change (or mark this entry with :warning:). Tokens that previously decrypted while explicitly declaring an unsupported alg or enc now return 400. Legacy no-AAD tokens and headers that omit those fields remain accepted, but token generators that emit misleading values must change to alg: dir and enc: A256GCM.

Comment thread CHANGELOG.md
- fix(etcd): do not block writes when the deployment role cannot be read [#13885](https://github.com/apache/apisix/pull/13885)
- fix(standalone): stop aborting stream connections before the first config arrives [#13855](https://github.com/apache/apisix/pull/13855)
- fix(standalone): harden the declarative configuration paths: validate the shape of the request body instead of 500ing, log the parser error rather than the body (which can carry credentials and private keys), check a stream route's `superior_id` self reference during validation, and guard null deployment sections in the CLI [#13886](https://github.com/apache/apisix/pull/13886)
- fix(control): always report healthcheck nodes as a JSON array, so `nodes` and the top-level list of `/v1/healthcheck` are `[]` instead of `{}` when empty [#13891](https://github.com/apache/apisix/pull/13891)

@kayx23 kayx23 Sep 21, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This changes the JSON type of an existing Control API response from object to array when empty ({} to []). That is the correct contract, but strict clients or deserializers can still break on upgrade. Please mark it compatibility-sensitive in Change, or explicitly explain why this API shape correction is exempt from the warning policy.

Comment thread CHANGELOG.md

### Change

- :warning: feat(upstream): verify the upstream certificate against configurable CAs. `upstream.tls.verify` was only read by the `kafka` scheme and is now honoured for `https` and `grpcs` as well, so an upstream that already carried `verify: true` starts rejecting a certificate it cannot validate; `tls.ca_certs` picks the trust anchors per upstream [#13863](https://github.com/apache/apisix/pull/13863)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please also mention the Runtime dependency for this breaking change. The new behavior calls resty.apisix.upstream.set_ssl_verify and set_ssl_trusted_store; an older/custom Runtime that lacks either API returns 503 (need to build APISIX-Runtime...) before certificate validation. The 3.19 release tree pins APISIX Runtime 1.3.18, so official 3.19 packages are covered, but custom builds need the equivalent APIs.

Comment thread CHANGELOG.md

- :warning: feat(upstream): verify the upstream certificate against configurable CAs. `upstream.tls.verify` was only read by the `kafka` scheme and is now honoured for `https` and `grpcs` as well, so an upstream that already carried `verify: true` starts rejecting a certificate it cannot validate; `tls.ca_certs` picks the trust anchors per upstream [#13863](https://github.com/apache/apisix/pull/13863)
- :warning: fix(openid-connect): validate the introspection issuer. With an explicit `claim_validator.issuer.valid_issuers`, a successful remote introspection response must now carry a string `iss` matching one of them, or the request is rejected with 401; omit the allowlist to keep the previous behavior [#13916](https://github.com/apache/apisix/pull/13916)
- :warning: fix(batch-requests): bound aggregated response bodies. New `max_response_body_size` (1 MiB) and `max_response_body_size_total` (10 MiB) plugin metadata; a pipeline above either limit now returns 502 instead of the full aggregate [#13906](https://github.com/apache/apisix/pull/13906)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new limits are global plugin metadata written through /apisix/admin/plugin_metadata/batch-requests, not route-level fields. Also, the merged docs/en/latest/plugins/batch-requests.md and Chinese counterpart still omit max_response_body_size and max_response_body_size_total from the Metadata table and configuration example. Please add the two fields so operators can act on this breaking change without relying on the changelog alone.

Comment thread CHANGELOG.md
- feat(stream): match a stream route by several SNIs through the new `snis` field, mutually exclusive with `sni` [#13911](https://github.com/apache/apisix/pull/13911)
- feat(websocket): add the `ws`/`wss` upstream scheme, which proxies frames through APISIX itself and exposes the `ws_handshake`, `ws_client_frame`, `ws_upstream_frame` and `ws_close` plugin phases plus the `core.websocket` API [#13939](https://github.com/apache/apisix/pull/13939)
- feat(upstream): slow start for newly observed upstream nodes via `warm_up_conf` [#13941](https://github.com/apache/apisix/pull/13941)
- feat: improve API-driven standalone update reliability: workers report a per-entity configuration digest, `PUT /apisix/admin/configs` accepts a `wait` parameter and answers 200 once every worker has loaded the configuration (202 otherwise), and the shdict format carries the digest outside the JSON [#13904](https://github.com/apache/apisix/pull/13904)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

“Per-entity configuration digest” is misleading here. The implementation writes the same target X-Digest under each tracked resource-type key (routes, services, and so on) for every worker/subsystem; it does not compute a separate digest for each resource object. Consider “workers report application of the configuration digest for each tracked resource type.”

Comment thread CHANGELOG.md
### Plugins

- feat: add the `openapi-to-mcp` plugin, serving an HTTP API to MCP clients from its OpenAPI document over Streamable HTTP and HTTP+SSE [#13942](https://github.com/apache/apisix/pull/13942)
- feat(websocket): add the `websocket-proxy` plugin to customize proxy behaviors, starting with `client_max_payload_len` / `upstream_max_payload_len` for `ws`/`wss` upstreams [#13972](https://github.com/apache/apisix/pull/13972)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The implementation and PR rationale define these values as the maximum size of a single WebSocket frame, but the plugin schema and parts of docs/en/latest/plugins/websocket-proxy.md call them a message limit. Because fragmented messages can span multiple frames, please standardize the documentation on “frame” (and update the Chinese page) before the release notes rely on this behavior.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants