[{"url":"https://api.github.com/repos/openziti/ziti/releases/368828947","assets_url":"https://api.github.com/repos/openziti/ziti/releases/368828947/assets","upload_url":"https://uploads.github.com/repos/openziti/ziti/releases/368828947/assets{?name,label}","html_url":"https://github.com/openziti/ziti/releases/tag/v1.5.17","id":368828947,"author":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"node_id":"RE_kwDODVFMN84V--IT","tag_name":"v1.5.17","target_commitish":"main","name":"v1.5.17","draft":false,"immutable":false,"prerelease":true,"created_at":"2026-08-11T19:59:16Z","updated_at":"2026-08-11T20:11:59Z","published_at":"2026-08-11T20:11:59Z","assets":[{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/510590997","id":510590997,"node_id":"RA_kwDODVFMN84ebwAV","name":"checksums.sha256.txt","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"text/plain; charset=utf-8","state":"uploaded","size":758,"digest":"sha256:7024fff376c0a5020aeca3b8ae1581d24179f6ad5bdb0b38208041a4c920e54a","download_count":2,"created_at":"2026-08-11T20:11:57Z","updated_at":"2026-08-11T20:11:57Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.5.17/checksums.sha256.txt"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/510590992","id":510590992,"node_id":"RA_kwDODVFMN84ebwAQ","name":"sbom-v1.5.17.spdx.json","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/json","state":"uploaded","size":994181,"digest":"sha256:90537899a8cc29a04cede90b74e18aad58cad4a981a9c00353be4cafb4d7861a","download_count":2,"created_at":"2026-08-11T20:11:57Z","updated_at":"2026-08-11T20:11:57Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.5.17/sbom-v1.5.17.spdx.json"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/510590994","id":510590994,"node_id":"RA_kwDODVFMN84ebwAS","name":"source-v1.5.17.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":3401818,"digest":"sha256:d430ee9b68de38d20acb33a2dd1b1222656b373a42c17281fbc4d99b0b054b12","download_count":2,"created_at":"2026-08-11T20:11:57Z","updated_at":"2026-08-11T20:11:58Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.5.17/source-v1.5.17.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/510590957","id":510590957,"node_id":"RA_kwDODVFMN84ebv_t","name":"ziti-darwin-amd64-1.5.17.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":50968280,"digest":"sha256:2a06390dc18ecc6147a58beb05b46b51b9a0796e231d45de06988333491edf5b","download_count":2,"created_at":"2026-08-11T20:11:54Z","updated_at":"2026-08-11T20:11:56Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.5.17/ziti-darwin-amd64-1.5.17.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/510590956","id":510590956,"node_id":"RA_kwDODVFMN84ebv_s","name":"ziti-darwin-arm64-1.5.17.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":47388774,"digest":"sha256:f26dcaf9fe10539ee3f73f317b3d53cbf527d4aeb83f369a52d9192eded787ce","download_count":2,"created_at":"2026-08-11T20:11:54Z","updated_at":"2026-08-11T20:11:56Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.5.17/ziti-darwin-arm64-1.5.17.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/510590959","id":510590959,"node_id":"RA_kwDODVFMN84ebv_v","name":"ziti-linux-amd64-1.5.17.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":52188844,"digest":"sha256:8c4457b2568a73387e3c791efb042c40b0c562f7092b9451ee00286e39756dd6","download_count":5,"created_at":"2026-08-11T20:11:54Z","updated_at":"2026-08-11T20:11:57Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.5.17/ziti-linux-amd64-1.5.17.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/510590955","id":510590955,"node_id":"RA_kwDODVFMN84ebv_r","name":"ziti-linux-arm-1.5.17.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":48739644,"digest":"sha256:1c8a18e3122bceacc8ac812edd177c6239df6a5235ea008b8cb70c411359eda9","download_count":2,"created_at":"2026-08-11T20:11:54Z","updated_at":"2026-08-11T20:11:56Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.5.17/ziti-linux-arm-1.5.17.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/510590989","id":510590989,"node_id":"RA_kwDODVFMN84ebwAN","name":"ziti-linux-arm64-1.5.17.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":48957940,"digest":"sha256:eff42d609f59c8b462fca40c0f9762932d11dd9a80969f3dd1ff160ee1bdbc3c","download_count":4,"created_at":"2026-08-11T20:11:56Z","updated_at":"2026-08-11T20:11:58Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.5.17/ziti-linux-arm64-1.5.17.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/510590958","id":510590958,"node_id":"RA_kwDODVFMN84ebv_u","name":"ziti-windows-amd64-1.5.17.zip","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/zip","state":"uploaded","size":41981519,"digest":"sha256:af262f84497e6ffbbfda2a75b533b30f553b66c0fb142aecdd7f67503bdf0e8c","download_count":3,"created_at":"2026-08-11T20:11:54Z","updated_at":"2026-08-11T20:11:56Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.5.17/ziti-windows-amd64-1.5.17.zip"}],"tarball_url":"https://api.github.com/repos/openziti/ziti/tarball/v1.5.17","zipball_url":"https://api.github.com/repos/openziti/ziti/zipball/v1.5.17","body":"# Release 1.5.17\n\n## What's New\n\nUpdate libraries and build with the latest Go version.\n\n"},{"url":"https://api.github.com/repos/openziti/ziti/releases/363420809","assets_url":"https://api.github.com/repos/openziti/ziti/releases/363420809/assets","upload_url":"https://uploads.github.com/repos/openziti/ziti/releases/363420809/assets{?name,label}","html_url":"https://github.com/openziti/ziti/releases/tag/v1.6.19","id":363420809,"author":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"node_id":"RE_kwDODVFMN84VqVyJ","tag_name":"v1.6.19","target_commitish":"main","name":"v1.6.19","draft":false,"immutable":false,"prerelease":false,"created_at":"2026-08-01T03:35:19Z","updated_at":"2026-08-03T15:55:20Z","published_at":"2026-08-01T03:46:01Z","assets":[{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/497509421","id":497509421,"node_id":"RA_kwDODVFMN84dp2Qt","name":"checksums.sha256.txt","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"text/plain; charset=utf-8","state":"uploaded","size":758,"digest":"sha256:7b181d252aebae5ede72ac841f6bfd19cb6586dfe7a03e8146ce2bbffc44637b","download_count":5,"created_at":"2026-08-01T03:45:59Z","updated_at":"2026-08-01T03:46:00Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.6.19/checksums.sha256.txt"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/497509416","id":497509416,"node_id":"RA_kwDODVFMN84dp2Qo","name":"sbom-v1.6.19.spdx.json","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/json","state":"uploaded","size":975699,"digest":"sha256:a85a2cd542fc65fc20843b1254d23f80119cf704ecba3781650366dc6fd332a0","download_count":3,"created_at":"2026-08-01T03:45:59Z","updated_at":"2026-08-01T03:45:59Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.6.19/sbom-v1.6.19.spdx.json"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/497509420","id":497509420,"node_id":"RA_kwDODVFMN84dp2Qs","name":"source-v1.6.19.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":3535504,"digest":"sha256:ab7e64c9503fca6c8e73114831030a6c5988444a25884a2dd6a8c06aae62f63c","download_count":6,"created_at":"2026-08-01T03:45:59Z","updated_at":"2026-08-01T03:46:00Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.6.19/source-v1.6.19.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/497509327","id":497509327,"node_id":"RA_kwDODVFMN84dp2PP","name":"ziti-darwin-amd64-1.6.19.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":53474092,"digest":"sha256:f4d92c0f7746cb895549faf6339f03c51fe810be922fa56ead3a0c8c72603e90","download_count":39,"created_at":"2026-08-01T03:45:54Z","updated_at":"2026-08-01T03:45:59Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.6.19/ziti-darwin-amd64-1.6.19.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/497509328","id":497509328,"node_id":"RA_kwDODVFMN84dp2PQ","name":"ziti-darwin-arm64-1.6.19.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":49768678,"digest":"sha256:7b6330da8f7ece815099816ea5d6a0ee5273946ab6524bd7ef54afd9a31eab51","download_count":68,"created_at":"2026-08-01T03:45:54Z","updated_at":"2026-08-01T03:45:59Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.6.19/ziti-darwin-arm64-1.6.19.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/497509329","id":497509329,"node_id":"RA_kwDODVFMN84dp2PR","name":"ziti-linux-amd64-1.6.19.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":54717043,"digest":"sha256:78ee212f35829144d8a777199a5218338a7551e7f2bc55db35470050a3a3c2e6","download_count":101,"created_at":"2026-08-01T03:45:54Z","updated_at":"2026-08-01T03:45:59Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.6.19/ziti-linux-amd64-1.6.19.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/497509331","id":497509331,"node_id":"RA_kwDODVFMN84dp2PT","name":"ziti-linux-arm-1.6.19.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":51179293,"digest":"sha256:a81269213e3ee47f7c1156c9c2850ea859d78cf3410b8e9bc1379e416dd4b15b","download_count":11,"created_at":"2026-08-01T03:45:54Z","updated_at":"2026-08-01T03:45:59Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.6.19/ziti-linux-arm-1.6.19.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/497509404","id":497509404,"node_id":"RA_kwDODVFMN84dp2Qc","name":"ziti-linux-arm64-1.6.19.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":51226710,"digest":"sha256:965540c358fbfa665c901d3cfc33865aed14ea6b8fe5496a8bb3049d4b8e54ba","download_count":9,"created_at":"2026-08-01T03:45:59Z","updated_at":"2026-08-01T03:46:01Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.6.19/ziti-linux-arm64-1.6.19.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/497509332","id":497509332,"node_id":"RA_kwDODVFMN84dp2PU","name":"ziti-windows-amd64-1.6.19.zip","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/zip","state":"uploaded","size":44224531,"digest":"sha256:f00ae68d02943cc5257023e3686736cd67699193b9c8a7ecb558aec35d35f82b","download_count":103,"created_at":"2026-08-01T03:45:54Z","updated_at":"2026-08-01T03:45:58Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.6.19/ziti-windows-amd64-1.6.19.zip"}],"tarball_url":"https://api.github.com/repos/openziti/ziti/tarball/v1.6.19","zipball_url":"https://api.github.com/repos/openziti/ziti/zipball/v1.6.19","body":"# Release 1.6.19\r\n\r\n## What's New\r\n\r\n* Fixes a deadlock that can wedge the controller\r\n\r\n## Component Updates and Bug Fixes\r\n\r\n* github.com/openziti/ziti: [v1.6.18 -> v1.6.19](https://github.com/openziti/ziti/compare/v1.6.18...v1.6.19)\r\n    * [Issue #4208](https://github.com/openziti/ziti/issues/4208) - [Backport-1.6] Lock order inversion in ConnectionTracker deadlocks the controller\r\n\r\n"},{"url":"https://api.github.com/repos/openziti/ziti/releases/361368514","assets_url":"https://api.github.com/repos/openziti/ziti/releases/361368514/assets","upload_url":"https://uploads.github.com/repos/openziti/ziti/releases/361368514/assets{?name,label}","html_url":"https://github.com/openziti/ziti/releases/tag/v2.1.0-pre1","id":361368514,"author":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"node_id":"RE_kwDODVFMN84VigvC","tag_name":"v2.1.0-pre1","target_commitish":"main","name":"v2.1.0-pre1","draft":false,"immutable":false,"prerelease":true,"created_at":"2026-07-28T20:32:57Z","updated_at":"2026-07-28T20:51:16Z","published_at":"2026-07-28T20:51:16Z","assets":[{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/493184775","id":493184775,"node_id":"RA_kwDODVFMN84dZWcH","name":"checksums.sha256.txt","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"text/plain; charset=utf-8","state":"uploaded","size":790,"digest":"sha256:480bcc8f3e4bdc9a52da0442643da7b002137ca0b102a7d86f85973293e05d63","download_count":3,"created_at":"2026-07-28T20:51:14Z","updated_at":"2026-07-28T20:51:14Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.1.0-pre1/checksums.sha256.txt"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/493184771","id":493184771,"node_id":"RA_kwDODVFMN84dZWcD","name":"sbom-v2.1.0-pre1.spdx.json","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/json","state":"uploaded","size":975699,"digest":"sha256:4e864522ce1924cbfd5abbbd05af2f518eca3de7bce4ac05d94a0a833f8ab8ba","download_count":2,"created_at":"2026-07-28T20:51:14Z","updated_at":"2026-07-28T20:51:14Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.1.0-pre1/sbom-v2.1.0-pre1.spdx.json"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/493184772","id":493184772,"node_id":"RA_kwDODVFMN84dZWcE","name":"source-v2.1.0-pre1.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":4459865,"digest":"sha256:02daeccf860f102d1683b4b106e8313787e72dc785bb0acc14101455ad01f270","download_count":4,"created_at":"2026-07-28T20:51:14Z","updated_at":"2026-07-28T20:51:14Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.1.0-pre1/source-v2.1.0-pre1.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/493184705","id":493184705,"node_id":"RA_kwDODVFMN84dZWbB","name":"ziti-darwin-amd64-2.1.0-pre1.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":56527039,"digest":"sha256:4272b7dcacc9e7fe5d0376f2139e6c9775ddca75b12ee3af3582d97e74c154d1","download_count":5,"created_at":"2026-07-28T20:51:11Z","updated_at":"2026-07-28T20:51:14Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.1.0-pre1/ziti-darwin-amd64-2.1.0-pre1.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/493184711","id":493184711,"node_id":"RA_kwDODVFMN84dZWbH","name":"ziti-darwin-arm64-2.1.0-pre1.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":52692653,"digest":"sha256:1bc0dca0a39be93d2c66a275b8b8ff08f5536a8072fa972c1fb84d6303e14a4b","download_count":4,"created_at":"2026-07-28T20:51:11Z","updated_at":"2026-07-28T20:51:15Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.1.0-pre1/ziti-darwin-arm64-2.1.0-pre1.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/493184706","id":493184706,"node_id":"RA_kwDODVFMN84dZWbC","name":"ziti-linux-amd64-2.1.0-pre1.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":57940219,"digest":"sha256:69bff2d5963cc6f8c33b7b24e21404fad97696f50ffc87aad3a769476d0cb4fc","download_count":13,"created_at":"2026-07-28T20:51:11Z","updated_at":"2026-07-28T20:51:14Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.1.0-pre1/ziti-linux-amd64-2.1.0-pre1.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/493184703","id":493184703,"node_id":"RA_kwDODVFMN84dZWa_","name":"ziti-linux-arm-2.1.0-pre1.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":54199379,"digest":"sha256:37035ed362291e6b4a9342f4e10643605a6cd156a66b59d1bc5a12356d94c102","download_count":5,"created_at":"2026-07-28T20:51:11Z","updated_at":"2026-07-28T20:51:13Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.1.0-pre1/ziti-linux-arm-2.1.0-pre1.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/493184769","id":493184769,"node_id":"RA_kwDODVFMN84dZWcB","name":"ziti-linux-arm64-2.1.0-pre1.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":54397300,"digest":"sha256:15633f9429159a750e8c0358db92c866b828b75cef44bc9720dba1838b933e30","download_count":5,"created_at":"2026-07-28T20:51:14Z","updated_at":"2026-07-28T20:51:15Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.1.0-pre1/ziti-linux-arm64-2.1.0-pre1.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/493184704","id":493184704,"node_id":"RA_kwDODVFMN84dZWbA","name":"ziti-windows-amd64-2.1.0-pre1.zip","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/zip","state":"uploaded","size":46798790,"digest":"sha256:2d7dfa9962c3df3739b9c52b6218c66b964b452223ea9c275a51b5a01885fbf7","download_count":8,"created_at":"2026-07-28T20:51:11Z","updated_at":"2026-07-28T20:51:14Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.1.0-pre1/ziti-windows-amd64-2.1.0-pre1.zip"}],"tarball_url":"https://api.github.com/repos/openziti/ziti/tarball/v2.1.0-pre1","zipball_url":"https://api.github.com/repos/openziti/ziti/zipball/v2.1.0-pre1","body":"# Release 2.1.0\n\n## What's New\n\n* [Connect-V2: Sessionless SDK Dial](#connect-v2-sessionless-sdk-dial) - SDKs can dial services without a per-service controller session; the router authorizes the dial locally against the Router Data Model\n* [ZAC Bootstrapping CLI](#zac-bootstrapping-cli) - CLI commands to download, configure, and serve the Ziti Admin Console without hand-editing YAML\n* [Verify Traffic with ext-jwt-signers](#verify-traffic-with-ext-jwt-signers) - `ziti ops verify traffic` can authenticate with an ext-jwt-signer (OIDC) to test the certless ephemeral-cert path\n* [Cluster Quorum Recovery](#cluster_quorum_recovery) - A mechanism for recovering clusters that have irrevocably lost the ability to form a quorum\n* [Quickstart Cluster](#quickstart-cluster) - `ziti run quickstart cluster` brings up a multi-node HA cluster in a single command for testing and development and learning\n* [Fully Connected Controller Mesh](#fully-connected-controller-mesh) - Controllers now proactively keep the cluster mesh fully connected\n* [Config Type Target Field](#config-type-target-field) - Config types now have a target field indicating whether they apply to services, routers or other entities\n* [Wildcard OIDC Issuers](#wildcard-oidc-issuers) - Controllers with a wildcard server-certificate SAN can serve OIDC for explicitly allow-listed hostnames\n* [Router Configs](#router-configs) - Allow routers to have a list of associated configs\n* [Multiple LAN Interfaces for tproxy](#multiple-lan-interfaces-for-tproxy) - `lanIf` now accepts a single interface or a list of interfaces\n* [Multiple Resolver Addresses for tproxy](#multiple-resolver-addresses-for-tproxy) - `resolver` now accepts a single address or a list of addresses\n* [DNS Upstream Query Modes](#dns-upstream-query-modes) - choose how multiple DNS upstreams are queried: parallel fan-out (default) or serial fail-through\n* [Logging Now Uses slog with an Async Handler](#logging-now-uses-slog-with-an-async-handler) - Logging moves to Go's `log/slog` behind an asynchronous sink; output is unchanged by default, with new flags to tune buffering\n* [Security Advisories](#security-advisories) - Includes the two control-plane certificate validation fixes first released in 2.0.2\n\n## Security Advisories\n\nThis release includes the two control-plane certificate and identity validation fixes first released in\n2.0.2 and 1.6.18. Anyone upgrading from 2.0.1 or earlier is picking them up here for the first time. See\nthe linked GitHub Security Advisories for full details, impact, and affected versions.\n\n* [GHSA-mrpr-756c-xm47](https://github.com/openziti/ziti/security/advisories/GHSA-mrpr-756c-xm47) (Critical) - Improper peer certificate validation on the controller\n  cluster mesh, router links, and metrics endpoint. TLS peer checks accepted a connection when any presented\n  certificate chained to the trusted CA while taking the peer identity from the leaf certificate, allowing a\n  peer to be admitted under a forged identity without possessing a trusted key. On HA/clustered controllers\n  this allows joining the controller cluster as an arbitrary controller.\n* [GHSA-cc5m-7mhm-xh9f](https://github.com/openziti/ziti/security/advisories/GHSA-cc5m-7mhm-xh9f) (Medium) - Control-channel connections carrying a channel-type header\n  bypassed router certificate and identity verification, allowing an attacker that can reach the controller\n  control port to be admitted as an arbitrary router identity and manipulate that router's fabric terminators,\n  faults, and circuit routing. Impact is limited to router data model metadata (service and identity names) and\n  control-plane manipulation; it does not by itself grant access to the services the network protects.\n\n## Connect-V2: Sessionless SDK Dial\n\nZiti SDKs built on sdk-golang v2 can now dial a service without first obtaining a\nper-service session token from the controller. With Connect-V2 the router authorizes the\ndial locally against its copy of the Router Data Model, the same policy and posture data\nthe controller already distributes. Dropping the per-dial session round-trip to the\ncontroller lowers dial latency and controller load, most noticeably for identities that\ndial many services.\n\nThere is nothing to configure. Connect-V2 is negotiated automatically: a router advertises\nthe capability, an SDK that supports it uses the new path, and older SDKs transparently\ncontinue to use the session-based dial. Policy and posture checks are still enforced on\nevery dial; they simply run against the Router Data Model on the router instead of during\nsession creation on the controller.\n\nConnect-V2 requires both a router and an SDK from this release (or newer). Mixed networks\nare fine: a dial uses Connect-V2 only when both ends support it, and otherwise falls back\nto the session-based path.\n\n## ZAC Bootstrapping CLI\n\nThree new commands make it easy to get the Ziti Admin Console running without manual file editing.\n\n**Download ZAC:**\n\n```\nziti ops console download [version] --location <dir>\n```\n\nDownloads a ZAC release from GitHub and extracts it into the given directory. `version` defaults to\n`latest`. The target directory must be empty or not yet exist; the command never removes or overwrites\nexisting files.\n\n**Configure a controller to serve it:**\n\n```\nziti ops console configure <controller-config.yml> --all --location <dir>\n```\n\nEdits the controller config YAML in place, adding or updating the `spa` web-listener binding so the\ncontroller serves ZAC from the given directory. Selects listeners with `--all` or `--name`; prompts\ninteractively when neither is given. The file's comments and structure are preserved.\n\n**Serve it locally:**\n\nIf you prefer to serve the console locally rather than have a controller host it, you can serve ZAC\nstraight from the ziti CLI:\n\n```\nziti run console --location <dir>\nziti run console --version latest\n```\n\nServes the ZAC SPA over HTTPS on `127.0.0.1:8443`. Generates a self-signed cert automatically if none\nis supplied. The browser points ZAC at whatever controller you choose inside the console itself.\n\nTo serve with your own certificate on a specific address and port:\n\n```\nziti run console --location <dir> --bind-address 0.0.0.0 --port 9443 \\\n  --tls-cert ./server.pem --tls-key ./server.key\n```\n\n**Bootstrap it with the quickstart:**\n\n```\nziti run quickstart --zac\n```\n\nAdding `--zac` to the quickstart downloads ZAC and configures the controller to serve it in one step,\nso the console is available at `https://<ctrl-address>:<ctrl-port>/zac` as soon as the network is up.\nThe assets install under `<home>/console` by default (override with `--zac-location`), and\n`--zac-version` selects the release (defaults to `latest`). Re-running against an existing `--home`\nreconciles the binding, so adding `--zac` on a later run enables the console on a network that was first\nbrought up without it. `ziti run quickstart cluster --zac` installs the assets once and serves the\nconsole from every node.\n\nTwo related quickstart re-run fixes ship alongside this:\n\n* Re-running against an already-initialized `--home` now adopts the controller and router ports/addresses\n  the environment was first created with, instead of falling back to the flag defaults. Previously a\n  re-run that omitted `--ctrl-port` would wait on the default port while the controller listened on the\n  original one.\n* Re-running against an existing `--home` with a flag that only takes effect when the environment is\n  first created (for example `--ctrl-port`) now reports that the flag was ignored, rather than silently\n  dropping it.\n\n## Verify Traffic with ext-jwt-signers\n\n`ziti ops verify traffic` can now authenticate with an ext-jwt-signer (OIDC), so the certless\nephemeral-certificate path can be traffic-tested.\n\n* `--ext-jwt-signer <name>` selects the signer to authenticate with and generate a cert from.\n* `--ext-jwt-redirect-url` sets the OIDC redirect URL (default `http://localhost:20314/auth/callback`).\n\n## Cluster Quorum Recovery\n\nA new offline CLI command, `ziti ops cluster recover <controller-config>`, lets\noperators rebuild a stuck HA controller cluster after losing quorum. Use it when\nenough controllers are permanently gone that `ziti ops cluster add` and\n`ziti ops cluster remove` fail with \"no leader\" — for example, a 2-node cluster\nwhere one node is unrecoverable, or a 3-node cluster that lost two nodes at once.\n\nThe command must be run while the surviving controller process is stopped. It\nreads the same controller config the controller would, opens the raft data\ndirectory, calls `raft.RecoverCluster` to force the configuration down to a\nsingle local node, and aligns the FSM-tracked member list and snapshot data so\nno stale peers leak through on restart. After it succeeds, restart the\ncontroller and add new peers normally with `ziti ops cluster add`.\n\n### End-to-End Encryption (e2ee) Improvements\n\n* Add support for negotiating e2ee scheme during Dial/Accept handshake\n* Allow hosting-side crypto material to be generated on per connection basis (instead of per terminator)\n\n\n## Quickstart Cluster\n\n`ziti run quickstart cluster` stands up a multi-node HA controller cluster with a single command, for\ntesting, learning, and local development. It launches one quickstart child process per node (default 3,\nminimum 3, configurable with `--size`), initializes the first\nnode, joins the rest, and prints a banner once the whole cluster is online listing each node's controller\naddress, router address, process id, and per-node log file.\n\nEach node runs as its own operating-system process, so you can stop, restart, or attach a debugger to any\nsingle node to explore HA behavior without disturbing the others. The banner prints the exact\n`ziti run quickstart` command needed to start each node by hand.\n\nLifecycle mirrors the single-node quickstart. Pass `--home` for a persistent cluster you can stop and start\nagain: restarting against an existing `--home` rejoins the existing cluster rather than re-initializing it,\nand the nodes start together to re-form a quorum. Omit `--home` to run from a temporary directory that is\nremoved on a clean shutdown. Pressing Ctrl-C stops every node.\n\n## Fully Connected Controller Mesh\n\nIn an HA cluster, controllers form a mesh of channel connections that raft uses to\ncommunicate. Previously we followed the raft library's lead: connections were made as\nneeded to allow elections, and after that only the leader maintained connections to its\nfollowers. That is enough for raft itself, but it means most nodes have no direct link to\nmost other nodes.\n\nNow controllers keep the mesh fully connected. This gives better visibility into system\nstate from any node, not just the leader, and it provides a baseline for building\nadditional non-raft coordination features on top of the mesh.\n\nEach controller runs a `PeerDialer` that proactively dials every known cluster member and\nworks to keep the mesh fully connected. Failed dials are retried with exponential backoff,\nand when two controllers dial each other at the same time a deterministic tie-break (based\non their SPIFFE IDs) keeps a single connection rather than a redundant pair. The dialer's\nstate can be inspected with `ziti fabric inspect ctrl-peer-dialer`.\n\nThe dialer is tunable via a new optional `cluster.dialer` config section. All values have\ndefaults, so no configuration change is required:\n\n```yaml\ncluster:\n  dialer:\n    minRetryInterval: 1s     # minimum time between dial retries\n    maxRetryInterval: 1m     # maximum time between dial retries\n    retryBackoffFactor: 2.0  # multiplier applied to the retry interval after each failure\n    fastFailureWindow: 30s   # if a connection is lost within this window, apply backoff instead of resetting the retry delay\n    dialTimeout: 10s         # maximum time a single dial attempt may run\n    scanInterval: 30s        # period of the full scan that reconciles dial state against membership\n    queueCheckInterval: 5s   # how often expired entries are popped from the retry queue\n```\n\nA related `cluster.nonMemberGrace` setting (default `1m`) controls how long a leader will\nlet a TLS-valid but non-member controller stay connected to the mesh before dropping it.\nThis gives a controller that is being added to the cluster time to be accepted as a member\nbefore its connection is reaped.\n\n## Config Type Target Field\n\nConfig types now have an optional `target` field that indicates what kind of entity the config type\nis intended for. Valid values are `\"service\"`, `\"router\"`, and `\"other\"`. The field is set on creation\nand is immutable afterward.\n\nThis is the first step toward controller-managed router configuration. The `target` field lets us\ndistinguish between config types meant for services, config types meant for routers, and config types\nmeant for other purposes, which keeps UIs, APIs, and validation clean. See\n`doc/design/ctrl-managed-router-config.md` for the full design.\n\nA database migration sets `target = \"service\"` on all existing config types. Services and identity\nservice config overrides now require that referenced configs have a config type with\n`target = \"service\"`.\n\nThe CLI has been updated to support the new field:\n\n* `ziti edge create config-type` now accepts a `--target` flag\n* `ziti edge list config-types` now shows a `Target` column\n\n## Wildcard OIDC Issuers\n\nControllers can serve OIDC for hostnames covered by a wildcard server-certificate SAN. Prior to 2.1,\nwildcard SANs were excluded from the set of valid OIDC issuers, so `/oidc/*` requests to a\nwildcard-covered hostname returned `404`.\n\nWildcard SANs cannot be used as a literal OIDC issuer (`https://*.example.com/oidc` is not a usable URL).\nInstead, the `edge-oidc` API binding now accepts an `allowedHostnames` option listing the exact hostnames\n(covered by the wildcard) that may be served as issuers. If omitted, the wildcard contributes no issuers\nand the controller logs a warning at startup; a malformed entry (a non-string value, or one containing a\nwildcard character) is a startup error:\n\n```yaml\nweb:\n  - name: client-management\n    apis:\n      - binding: edge-oidc\n        options:\n          allowedHostnames:\n            - ctrl.example.com\n```\n\nEach entry must be an exact hostname (no patterns) that an active server-certificate SAN actually covers;\nentries are matched against wildcard SANs using standard X.509 hostname rules. The resulting OIDC issuers\nare therefore concrete, fixed hostnames, so the set of valid `iss` values stays closed. Concrete\n(non-wildcard) SANs continue to be served as issuers automatically and do not need to be listed.\n\n## Router Configs\n\nRouters (edge, transit, and fabric) now have a `configs` field that holds a list of config IDs the\nrouter should use. This is the second step toward controller-managed router configuration: routers\ncan now be associated with configs in the same way services already can.\n\nValidation rules:\n\n* Every config referenced by a router must use a config type with `target = \"router\"`. Configs\n  with `target = \"service\"` (or anything else) are rejected.\n* A router may reference at most one config per config type. Attempting to attach two configs of\n  the same type is rejected with a duplicate-config error naming both configs.\n* Deleting a config automatically removes it from the `configs` list of any router that referenced\n  it, so dangling references are not possible.\n\nThe `configs` field is exposed on router create, update, patch, and detail responses across the\nedge, transit, and fabric router REST APIs.\n\nThe CLI has been updated to support the new field:\n\n* `ziti edge create edge-router` accepts `--config <id>` (repeatable)\n* `ziti edge create transit-router` accepts `--config <id>` (repeatable)\n* `ziti edge update edge-router` accepts `--config <id>` to replace the router's config list\n* `ziti fabric create router` accepts `--config <id>` (repeatable)\n* `ziti fabric update router` accepts `--config <id>` to replace the router's config list\n\n**Status: in progress.** The `target` field, the router `configs` field, and the built-in\n`router.link.v1` config type together establish the data model and distribution path:\nconfig types can be targeted at routers, configs can be attached to routers, and the\ncontroller distributes router-targeted configs to the affected routers through the Router\nData Model, where a router-side registry receives them. Routers do not yet consume these\nconfigs to drive their runtime behavior, so controller-managed router configuration is not\nready for production use in this release. See `doc/design/ctrl-managed-router-config.md`\nfor the overall design.\n\n## Multiple LAN Interfaces for tproxy\n\nThe `lanIf` option in `xgress_edge_tunnel` tproxy configs now accepts either a\nsingle string (as before) or a YAML list of interface names. For each intercepted\nservice address the router inserts one iptables `ACCEPT` rule per configured\ninterface. Existing single-interface configs are unchanged.\n\n```yaml\n# single interface (unchanged)\n- binding: tunnel\n  options:\n    mode: tproxy\n    lanIf: enp0s5\n\n# multiple interfaces\n- binding: tunnel\n  options:\n    mode: tproxy\n    lanIf:\n      - enp0s5\n      - enp0s6\n```\n\nThe CLI `--lanIf` flag also accepts a comma-separated list or repeated flags:\n\n```bash\nziti tunnel tproxy --lanIf enp0s5,enp0s6\n# or\nziti tunnel tproxy --lanIf enp0s5 --lanIf enp0s6\n```\n\n## Multiple Resolver Addresses for tproxy\n\nThe `resolver` option in `xgress_edge_tunnel` tproxy configs now accepts either a\nsingle string (as before) or a YAML list of `udp://` addresses. A single shared\nresolver instance handles all listeners so hostname→IP mappings remain consistent\nacross interfaces. Existing single-address configs are unchanged.\n\n```yaml\n# single address (unchanged)\n- binding: tunnel\n  options:\n    mode: tproxy\n    resolver: udp://172.18.102.70:53\n\n# multiple addresses\n- binding: tunnel\n  options:\n    mode: tproxy\n    resolver:\n      - udp://172.18.102.70:53\n      - udp://192.168.10.1:53\n```\n\n## DNS Upstream Query Modes\n\nWhen more than one DNS upstream is configured for tproxy host-side resolution, the\nresolver previously always fanned out: every upstream was queried in parallel and\nthe first NOERROR response won. For a high-volume router with several upstreams this\nmultiplies outbound DNS traffic, since every request hits every upstream.\n\nA new `dnsUpstreamMode` option (router config) / `--dnsUpstreamMode` flag\n(`ziti tunnel`) controls how multiple upstreams are queried:\n\n* `parallel` (default) - query all upstreams at once; first NOERROR wins. Unchanged\n  behavior.\n* `serial` - query upstreams one at a time in order, starting at the first. Fail\n  through to the next only on timeout, transport error, or a non-NOERROR response.\n* `failover` - like `serial`, but the upstream that last answered is reused as the\n  starting point, so a healthy upstream isn't re-probed from the top on every query.\n* `random` - like `serial`, but each query starts at a randomly chosen upstream.\n\nIn all serial modes a healthy upstream costs exactly one outbound query regardless of\nhow many upstreams are configured. The single-upstream case is unaffected.\n\nIn a router's `xgress_edge_tunnel` tproxy config:\n\n```yaml\n- binding: tunnel\n  options:\n    mode: tproxy\n    dnsUpstreamMode: serial\n    dnsUpstream:\n      - udp://10.96.0.10:53\n      - tcp://8.8.8.8:53\n```\n\nThe standalone `ziti tunnel` gains a matching `--dnsUpstreamMode` flag (default\n`parallel`):\n\n```\nziti tunnel run --dnsUpstreamMode serial \\\n  --dnsUpstream udp://10.96.0.10:53 \\\n  --dnsUpstream tcp://8.8.8.8:53\n```\n\n## Logging Now Uses slog with an Async Handler\n\nThe controller, router, and `ziti tunnel` now log through Go's standard\n`log/slog` library behind a single asynchronous sink. By default the output is\nunchanged - the same human-readable format as before - so no configuration\nchange is required. Existing `pfxlog`/`logrus` log statements continue to work;\nthey are bridged into the new sink rather than rewritten, so the migration to\nslog is gradual and nothing is lost in the meantime.\n\nThe motivation is performance under load. Previously every log call contended on\na single process-wide formatter-plus-writer mutex, which could block many\ngoroutines at once. Writing is now handed to a background goroutine, off the hot\npath of the code doing the logging.\n\n### Asynchronous writes and dropped records\n\nBecause writes are buffered through a bounded queue, behavior under saturation\nis the one change worth knowing about:\n\n* Records at or above a configurable block threshold (default `warn`) block the\n  caller until there is room, so warnings, errors, and fatal messages are never\n  silently dropped.\n* Lower-priority records (`info`, `debug`, `trace`) are dropped when the queue is\n  full instead of blocking. Whenever drops occur, a summary line is emitted\n  periodically reporting how many records were dropped per level, so the loss is\n  always visible in the logs.\n\nUnder normal load nothing is dropped; this only engages when log volume outruns\nthe writer.\n\n### New CLI flags\n\n`ziti controller run`, `ziti router run`, and `ziti tunnel` gain three optional\nflags to tune the async sink. All have sensible defaults, so leaving them unset\npreserves current behavior:\n\n* `--log-queue-size` (default `4096`) - capacity of the async log queue.\n* `--log-block-threshold` (default `warn`) - lowest level that blocks rather than\n  dropping under saturation (`panic|fatal|error|warn|info|debug|trace`).\n* `--log-summary-interval` (default `5s`) - how often the dropped-record summary\n  line is emitted.\n\nThe existing `--log-formatter` flag is unchanged: `pfxlog` (default), `json`\n(unchanged JSON shape), and `text` (logrus-style `key=value`).\n\n### Per-channel log levels are slog-only\n\n`ziti agent set-channel-log-level <name> <level>` now adjusts only code that has\nbeen migrated to the new slog loggers. Call sites still using `pfxlog.Logger()`\nor `pfxlog.ChannelLogger(...)` continue to follow the global level, so because\nmost call sites are not yet migrated, per-channel overrides have limited reach\ntoday and expand as packages are converted. The global `ziti agent set-log-level\n<level>` still affects everything.\n\n## Component Updates and Bug Fixes\n\n* github.com/openziti/channel/v5: [v4.3.11 -> v5.0.15](https://github.com/openziti/channel/compare/v4.3.11...v5.0.15)\n    * [Issue #258](https://github.com/openziti/channel/issues/258) - Add a hook to inject hello headers derived from the peer's certificate\n    * [Issue #267](https://github.com/openziti/channel/issues/267) - NewSingleChannelWithUnderlay panics on underlays with nil headers (e.g. websocket)\n    * [Issue #269](https://github.com/openziti/channel/issues/269) - Make channel logging pluggable via injectable slog.Logger\n    * [Issue #264](https://github.com/openziti/channel/issues/264) - Multi-underlay group reconnect can reject-storm under load\n    * [Issue #265](https://github.com/openziti/channel/issues/265) - Support reconfiguring heartbeat intervals on a running channel\n    * [Issue #261](https://github.com/openziti/channel/issues/261) - Add ContentTypeReceiver, a self-describing receive handler\n    * [Issue #250](https://github.com/openziti/channel/issues/250) - BackoffDialPolicy cannot (re)establish a grouped channel: never sets IsFirstGroupConnection\n    * [Issue #252](https://github.com/openziti/channel/issues/252) - BackoffDialPolicy misclassifies multi-underlay constraint fill as short-lived/flapping\n    * [Issue #247](https://github.com/openziti/channel/issues/247) - Config.Binder exposes unexported `*channelImpl` in its public signature\n    * [Issue #255](https://github.com/openziti/channel/issues/255) - Flaky Test_MultiUnderlayChannels: CloseRandom can close the last required underlay\n    * [Issue #253](https://github.com/openziti/channel/issues/253) - Multi-underlay channel delays below-Min closure when a dial/backoff is in progress\n    * [Issue #246](https://github.com/openziti/channel/issues/246) - classic_dialer leaks underlay FD when hello handshake fails\n    * [Issue #241](https://github.com/openziti/channel/issues/241) - Allow calling LoadOptions on an Options instance\n    * [Issue #238](https://github.com/openziti/channel/issues/238) - Channelv5\n\n* github.com/openziti/edge-api: [v0.31.0 -> v0.35.2](https://github.com/openziti/edge-api/compare/v0.31.0...v0.35.2)\n    * [Issue #198](https://github.com/openziti/edge-api/issues/198) - Advertise edge router capabilities in the service edge-router list\n\n* github.com/openziti/foundation/v2: [v2.0.91 -> v2.0.99](https://github.com/openziti/foundation/compare/v2.0.91...v2.0.99)\n    * [Issue #489](https://github.com/openziti/foundation/issues/489) - Add graceful shutdown and idle-wait support to goroutines.Pool\n    * [Issue #488](https://github.com/openziti/foundation/issues/488) - Add package-level Fatal and SyncEmit helpers to logging\n    * [Issue #484](https://github.com/openziti/foundation/issues/484) - Add slog logging core (foundation/v2/logging) for upstream libraries\n\n* github.com/openziti/identity: [v1.0.129 -> v1.0.137](https://github.com/openziti/identity/compare/v1.0.129...v1.0.137)\n* github.com/openziti/runzmd: [v1.0.90 -> v1.0.91](https://github.com/openziti/runzmd/compare/v1.0.90...v1.0.91)\n* github.com/openziti/sdk-golang/v2: [v1.7.0 -> v2.0.0-pre3](https://github.com/openziti/sdk-golang/compare/v1.7.0...v2.0.0-pre3)\n    * [Issue #958](https://github.com/openziti/sdk-golang/issues/958) - ConnectV2 xgress client conn not marked closed on router-initiated teardown\n    * [Issue #967](https://github.com/openziti/sdk-golang/issues/967) - Move RouterCapabilityConnectV2 constant into edge_client.proto\n    * [Issue #951](https://github.com/openziti/sdk-golang/issues/951) - Add an SDK acceptance-test framework\n    * [Issue #952](https://github.com/openziti/sdk-golang/issues/952) - xgress client half-close not delivered to legacy edge hosts\n    * [Issue #936](https://github.com/openziti/sdk-golang/issues/936) - Implement Connect-V2: sessionless SDK dial\n    * [Issue #945](https://github.com/openziti/sdk-golang/issues/945) - Migrate to channel/v5\n    * [Issue #948](https://github.com/openziti/sdk-golang/issues/948) - AddControllerUrlsUpdateListener remover unsubscribes the wrong event\n    * [Issue #941](https://github.com/openziti/sdk-golang/issues/941) - Prep for channel v5: explicit receive handler registration, drop send priorities\n    * [Issue #924](https://github.com/openziti/sdk-golang/issues/924) - Make controller http timeout configurable, with a default of 30s\n    * [Issue #925](https://github.com/openziti/sdk-golang/issues/925) - Switch controllers on a broader set of errors\n    * [Issue #926](https://github.com/openziti/sdk-golang/issues/926) - Refresh OIDC token using a window to avoid race conditions and herding\n    * [Issue #927](https://github.com/openziti/sdk-golang/issues/927) - Apply exponential backoff to auth retry attempts\n    * [Issue #932](https://github.com/openziti/sdk-golang/issues/932) - API Session Certificate chain is not preserved\n\n* github.com/openziti/secretstream: [v0.1.49 -> v0.1.51](https://github.com/openziti/secretstream/compare/v0.1.49...v0.1.51)\n* github.com/openziti/transport/v2: [v2.0.215 -> v2.0.216](https://github.com/openziti/transport/compare/v2.0.215...v2.0.216)\n* github.com/openziti/xweb/v3: [v3.0.4 -> v3.0.5](https://github.com/openziti/xweb/compare/v3.0.4...v3.0.5)\n* github.com/openziti/ziti/v2: [v2.0.0 -> v2.1.0](https://github.com/openziti/ziti/compare/v2.0.0...v2.1.0)\n    * [Issue #4149](https://github.com/openziti/ziti/issues/4149) - upgrading a running 1.x controller/router to 2.x fails to create the service user\n    * [Issue #3990](https://github.com/openziti/ziti/issues/3990) - Expose service change subscriptions to external SDKs over protobuf\n    * [Issue #4108](https://github.com/openziti/ziti/issues/4108) - Controller retains bbolt-managed memory past transaction (create-circuit response SIGSEGV)\n    * [Issue #4067](https://github.com/openziti/ziti/issues/4067) - JWKS peer signer kid is undecodable raw bytes (should be hex) -- %s vs %x on sha1.Sum in oidc_auth/storage.go KeySet()\n    * [Issue #4071](https://github.com/openziti/ziti/issues/4071) - Unify router capabilities into a single shared bitmask across control and edge channels\n    * [Issue #3998](https://github.com/openziti/ziti/issues/3998) - Add router-side managed configuration registry\n    * [Issue #4069](https://github.com/openziti/ziti/issues/4069) - Controller can cache empty apiAddresses forever due to startup race between raft mesh and xweb config load\n    * [Issue #4066](https://github.com/openziti/ziti/issues/4066) - bootstrap zac for controller with quickstart\n    * [Issue #4060](https://github.com/openziti/ziti/issues/4060) - support ext-jwt-signer in ziti verify traffic\n    * [Issue #4036](https://github.com/openziti/ziti/issues/4036) - Own the metrics wire format (MetricsMessage) in ziti\n    * [Issue #4137](https://github.com/openziti/ziti/issues/4137) - ziti tunnel ignores --dnsSvcIpRange <!-- keep -->\n    * [Issue #4052](https://github.com/openziti/ziti/issues/4052) - ziti cli cached creds don't use refresh token\n    * [Issue #3881](https://github.com/openziti/ziti/issues/3881) - Add Capability for DNSUPSTREAMS to be used in serial\n    * [Issue #4035](https://github.com/openziti/ziti/issues/4035) - Controller /metrics endpoint produces duplicate TYPE declarations causing Prometheus to drop samples\n    * [Issue #4045](https://github.com/openziti/ziti/issues/4045) - Reduce controller link/router management lock contention under high link churn\n    * [Issue #3884](https://github.com/openziti/ziti/issues/3884) - Implement Connect-V2: sessionless SDK dial\n    * [Issue #3929](https://github.com/openziti/ziti/issues/3929) - [Backport-2.0] Router does not enforce api-session or identity revocations on live connections\n    * [Issue #3841](https://github.com/openziti/ziti/issues/3841) - Controller Cluster - new controllers must be able to be dialed by the leader in order to join successfully\n    * [Issue #3933](https://github.com/openziti/ziti/issues/3933) - edge enrollment: add the list of controllers to successful enrollment response\n    * [Issue #3992](https://github.com/openziti/ziti/issues/3992) - Overlay edge-oidc listener panics when its redirect_uri is not in the redirectURIs allow-list\n    * [Issue #4039](https://github.com/openziti/ziti/issues/4039) - cryptic x509 error when cached CA is stale\n    * [Issue #4010](https://github.com/openziti/ziti/issues/4010) - Non-admin identity with `enrollment` entity permission can create enrollments for any identity including admins, achieving full privilege escalation to admin\n    * [Issue #4011](https://github.com/openziti/ziti/issues/4011) - Router deletes terminators ~12m after creation when host SDK replies with wrong inspect content type\n    * [Issue #4002](https://github.com/openziti/ziti/issues/4002) - make quickstart easier - ZAC\n    * [Issue #4007](https://github.com/openziti/ziti/issues/4007) - add cluster option to ziti run quickstart\n    * [Issue #3983](https://github.com/openziti/ziti/issues/3983) - Migrate to channel/v5\n    * [Issue #3916](https://github.com/openziti/ziti/issues/3916) - Convert router/forwarder to slog\n    * [Issue #3976](https://github.com/openziti/ziti/issues/3976) - Distribute routers and router-target configs through the RDM\n    * [Issue #3974](https://github.com/openziti/ziti/issues/3974) - Add router.link.v1 config type\n    * [Issue #3994](https://github.com/openziti/ziti/issues/3994) - support additional addresses in deployments\n    * [Issue #3934](https://github.com/openziti/ziti/issues/3934) - Consolidate the fabric and edge services data stores\n    * [Issue #3910](https://github.com/openziti/ziti/issues/3910) - Install slog and route agent log-level callbacks through common/logging\n    * [Issue #3927](https://github.com/openziti/ziti/issues/3927) - Router does not enforce api-session or identity revocations on live connections; revoked OIDC sessions keep dialing/hosting until access-token expiry\n    * [Issue #3906](https://github.com/openziti/ziti/issues/3906) - Add named-logger registry, logrus bridge, and pfxlog-shape JSON\n    * [Issue #3904](https://github.com/openziti/ziti/issues/3904) - Add slog AsyncHandler in preparation for moving to slog for logging\n    * [Issue #3902](https://github.com/openziti/ziti/issues/3902) - Add agent IPC capability discovery and channel-based log-level commands\n    * [Issue #3893](https://github.com/openziti/ziti/issues/3893) - Import openziti/agent library into common/agent\n    * [Issue #3894](https://github.com/openziti/ziti/issues/3894) - Consolidate duplicated agent channel-upgrade code into common/agent\n    * [Issue #3952](https://github.com/openziti/ziti/issues/3952) - externalIdClaim on CA returns HTTP 500 with empty body for most matcher/parser combinations\n    * [Issue #3908](https://github.com/openziti/ziti/issues/3908) - Router posture-data updates don't revoke SDK-hosted xgress circuits or hosted terminators\n    * [Issue #3780](https://github.com/openziti/ziti/issues/3780) - Add configs field to routers\n    * [Issue #3961](https://github.com/openziti/ziti/issues/3961) - Router panics evaluating an AnyOf process posture check when client reports no process/OS data\n    * [Issue #1593](https://github.com/openziti/ziti/issues/1593) - Expanded attribute query support in management API; add policy attribute support and usage count\n    * [Issue #3949](https://github.com/openziti/ziti/issues/3949) - DeleteById swallows errors when firing change events\n    * [Issue #3867](https://github.com/openziti/ziti/issues/3867) - Tunneler skips iptables rules for services sharing an intercept hostname\n    * [Issue #3891](https://github.com/openziti/ziti/issues/3891) - oidc auth fails with wildcard server-cert SANs\n    * [Issue #3914](https://github.com/openziti/ziti/issues/3914) - ziti login fails with oidc + wildcard certs\n    * [Issue #3938](https://github.com/openziti/ziti/issues/3938) - Carry the link id in a link header instead of only in the channel identity token\n    * [Issue #3945](https://github.com/openziti/ziti/issues/3945) - Increase certificate serial number namespace to 159 bits\n    * [Issue #3942](https://github.com/openziti/ziti/issues/3942) - Prep for channel v5: bind handler invocation, send priorities\n    * [Issue #3920](https://github.com/openziti/ziti/issues/3920) - ziti pki create csr ignores --key-name flag\n    * [Issue #3744](https://github.com/openziti/ziti/issues/3744) - Add a target field to config type\n    * [Issue #3684](https://github.com/openziti/ziti/issues/3684) - Keep controller mesh fully connected, as much as possible\n    * [Issue #3864](https://github.com/openziti/ziti/issues/3864) - e2ee: allow hosting SDK to return e2ee public key in the dial response\n    * [Issue #3849](https://github.com/openziti/ziti/issues/3849) - Add a recover mechanism for when a controller cluster can't form a quorum\n\n\n"},{"url":"https://api.github.com/repos/openziti/ziti/releases/360674621","assets_url":"https://api.github.com/repos/openziti/ziti/releases/360674621/assets","upload_url":"https://uploads.github.com/repos/openziti/ziti/releases/360674621/assets{?name,label}","html_url":"https://github.com/openziti/ziti/releases/tag/v2.0.2","id":360674621,"author":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"node_id":"RE_kwDODVFMN84Vf3U9","tag_name":"v2.0.2","target_commitish":"main","name":"v2.0.2","draft":false,"immutable":false,"prerelease":false,"created_at":"2026-07-27T19:12:49Z","updated_at":"2026-08-07T19:18:21Z","published_at":"2026-07-27T19:57:47Z","assets":[{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/491865244","id":491865244,"node_id":"RA_kwDODVFMN84dUUSc","name":"checksums.sha256.txt","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"text/plain; charset=utf-8","state":"uploaded","size":750,"digest":"sha256:875c84a9cf35c8cdfc2fe94ab58b1ab02ae1e6f17fa10af66a71a68ddad8a7d1","download_count":9,"created_at":"2026-07-27T19:57:45Z","updated_at":"2026-07-27T19:57:45Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.2/checksums.sha256.txt"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/491865239","id":491865239,"node_id":"RA_kwDODVFMN84dUUSX","name":"sbom-v2.0.2.spdx.json","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/json","state":"uploaded","size":985041,"digest":"sha256:57bebaaa2794d1d9113787df8ec4405921597ddf26e141973c4f20f1f8319af8","download_count":3,"created_at":"2026-07-27T19:57:45Z","updated_at":"2026-07-27T19:57:45Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.2/sbom-v2.0.2.spdx.json"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/491865240","id":491865240,"node_id":"RA_kwDODVFMN84dUUSY","name":"source-v2.0.2.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":4042893,"digest":"sha256:4efad497168561b694ed257134466598621eb9c07224f8c595e915f5ed32044c","download_count":8,"created_at":"2026-07-27T19:57:45Z","updated_at":"2026-07-27T19:57:45Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.2/source-v2.0.2.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/491865189","id":491865189,"node_id":"RA_kwDODVFMN84dUURl","name":"ziti-darwin-amd64-2.0.2.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":55130936,"digest":"sha256:fcf5e5fec05acec34bc07d85fc2ab4678f81ff75f798f41cf18e12c992970d62","download_count":36,"created_at":"2026-07-27T19:57:43Z","updated_at":"2026-07-27T19:57:45Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.2/ziti-darwin-amd64-2.0.2.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/491865191","id":491865191,"node_id":"RA_kwDODVFMN84dUURn","name":"ziti-darwin-arm64-2.0.2.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":51365383,"digest":"sha256:e6e5a2a0e25149208a6eca89abbef07d52e962a4e685f1753f7413c4f094edac","download_count":68,"created_at":"2026-07-27T19:57:43Z","updated_at":"2026-07-27T19:57:45Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.2/ziti-darwin-arm64-2.0.2.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/491865193","id":491865193,"node_id":"RA_kwDODVFMN84dUURp","name":"ziti-linux-amd64-2.0.2.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":56443717,"digest":"sha256:9c8ea7caa43f3b07285d4e473289efb52471bd769db512a55be7a166e7a46636","download_count":633,"created_at":"2026-07-27T19:57:43Z","updated_at":"2026-07-27T19:57:45Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.2/ziti-linux-amd64-2.0.2.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/491865190","id":491865190,"node_id":"RA_kwDODVFMN84dUURm","name":"ziti-linux-arm-2.0.2.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":52824374,"digest":"sha256:f64a77ff9335725c21aa8f49bd82fbaf9d897268dfbda4d45d50722a00658ce2","download_count":9,"created_at":"2026-07-27T19:57:43Z","updated_at":"2026-07-27T19:57:45Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.2/ziti-linux-arm-2.0.2.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/491865227","id":491865227,"node_id":"RA_kwDODVFMN84dUUSL","name":"ziti-linux-arm64-2.0.2.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":52859119,"digest":"sha256:156c5f12c44d8af2af094290f3c855d687933bc889c415a55c2c755942073018","download_count":242,"created_at":"2026-07-27T19:57:45Z","updated_at":"2026-07-27T19:57:46Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.2/ziti-linux-arm64-2.0.2.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/491865192","id":491865192,"node_id":"RA_kwDODVFMN84dUURo","name":"ziti-windows-amd64-2.0.2.zip","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/zip","state":"uploaded","size":45632592,"digest":"sha256:b16b696630dd2857deb41920b4ef92a378c278e9532a75c2ccb6c4b0e077b0dd","download_count":136,"created_at":"2026-07-27T19:57:43Z","updated_at":"2026-07-27T19:57:45Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.2/ziti-windows-amd64-2.0.2.zip"}],"tarball_url":"https://api.github.com/repos/openziti/ziti/tarball/v2.0.2","zipball_url":"https://api.github.com/repos/openziti/ziti/zipball/v2.0.2","body":"# Release 2.0.2\r\n\r\n## What's New\r\n\r\n* Security fixes (see Security Advisories below)\r\n* Bug fixes\r\n\r\n## Security Advisories\r\n\r\nThis release addresses two control-plane certificate and identity validation vulnerabilities. See the linked\r\nGitHub Security Advisories for full details, impact, and affected versions.\r\n\r\n* [GHSA-mrpr-756c-xm47](https://github.com/openziti/ziti/security/advisories/GHSA-mrpr-756c-xm47) (CVE pending) (Critical) - Improper peer certificate validation on the controller\r\n  cluster mesh, router links, and metrics endpoint. TLS peer checks accepted a connection when any presented\r\n  certificate chained to the trusted CA while taking the peer identity from the leaf certificate, allowing a\r\n  peer to be admitted under a forged identity without possessing a trusted key. On HA/clustered controllers\r\n  this allows joining the controller cluster as an arbitrary controller.\r\n* [GHSA-cc5m-7mhm-xh9f](https://github.com/openziti/ziti/security/advisories/GHSA-cc5m-7mhm-xh9f) (CVE pending) (Medium) - Control-channel connections carrying a channel-type header\r\n  bypassed router certificate and identity verification, allowing an attacker that can reach the controller\r\n  control port to be admitted as an arbitrary router identity and manipulate that router's fabric terminators,\r\n  faults, and circuit routing. Impact is limited to router data model metadata (service and identity names) and\r\n  control-plane manipulation; it does not by itself grant access to the services the network protects.\r\n\r\n## Component Updates and Bug Fixes\r\n\r\n* github.com/openziti/ziti/v2: [v2.0.1 -> v2.0.2](https://github.com/openziti/ziti/compare/v2.0.0...v2.0.1)\r\n  * [Issue #4136](https://github.com/openziti/ziti/issues/4136) - [Backport-2.0] ziti tunnel ignores --dnsSvcIpRange\r\n  * [Issue #4149](https://github.com/openziti/ziti/issues/4149) - [Backport-2.0] Upgrading a running 1.x controller/router to 2.x fails to create the service user\r\n  * [Issue #4108](https://github.com/openziti/ziti/issues/4108) - Fix controller panic / potential data corruption by copying terminator peer data, instance secret, and eventual event data out of bolt-managed memory\r\n\r\n"},{"url":"https://api.github.com/repos/openziti/ziti/releases/360676057","assets_url":"https://api.github.com/repos/openziti/ziti/releases/360676057/assets","upload_url":"https://uploads.github.com/repos/openziti/ziti/releases/360676057/assets{?name,label}","html_url":"https://github.com/openziti/ziti/releases/tag/v1.6.18","id":360676057,"author":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"node_id":"RE_kwDODVFMN84Vf3rZ","tag_name":"v1.6.18","target_commitish":"main","name":"v1.6.18","draft":false,"immutable":false,"prerelease":false,"created_at":"2026-07-27T19:13:22Z","updated_at":"2026-08-03T15:43:50Z","published_at":"2026-07-27T20:00:49Z","assets":[{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/491867898","id":491867898,"node_id":"RA_kwDODVFMN84dUU76","name":"checksums.sha256.txt","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"text/plain; charset=utf-8","state":"uploaded","size":758,"digest":"sha256:237dc880e12d5073f2188ebfd9497bd9cb0fc69c3d127aee98f5ddf50cd2ba7b","download_count":2,"created_at":"2026-07-27T20:00:48Z","updated_at":"2026-07-27T20:00:48Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.6.18/checksums.sha256.txt"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/491867885","id":491867885,"node_id":"RA_kwDODVFMN84dUU7t","name":"sbom-v1.6.18.spdx.json","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/json","state":"uploaded","size":985041,"digest":"sha256:c815ab644b52a52fb4149d220edd26dbd41dc9d95566d85ce79ffba2673ee5c2","download_count":2,"created_at":"2026-07-27T20:00:48Z","updated_at":"2026-07-27T20:00:48Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.6.18/sbom-v1.6.18.spdx.json"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/491867890","id":491867890,"node_id":"RA_kwDODVFMN84dUU7y","name":"source-v1.6.18.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":3532667,"digest":"sha256:2f4ce83b506b21e407110b2664b7c0bf5fd1abe2e1f979b4c8de43d269b7a3cd","download_count":3,"created_at":"2026-07-27T20:00:48Z","updated_at":"2026-07-27T20:00:48Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.6.18/source-v1.6.18.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/491867884","id":491867884,"node_id":"RA_kwDODVFMN84dUU7s","name":"ziti-darwin-amd64-1.6.18.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":53475147,"digest":"sha256:b1b13310e376ef0eb712eca46d7084f2b3233d924212b32a07e2ae7cfd5f668a","download_count":9,"created_at":"2026-07-27T20:00:48Z","updated_at":"2026-07-27T20:00:49Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.6.18/ziti-darwin-amd64-1.6.18.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/491867853","id":491867853,"node_id":"RA_kwDODVFMN84dUU7N","name":"ziti-darwin-arm64-1.6.18.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":49762655,"digest":"sha256:3e10a9154cc2567bbac115c70ca93c24a3f03197db6fb9da36380230db6b568f","download_count":9,"created_at":"2026-07-27T20:00:45Z","updated_at":"2026-07-27T20:00:48Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.6.18/ziti-darwin-arm64-1.6.18.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/491867852","id":491867852,"node_id":"RA_kwDODVFMN84dUU7M","name":"ziti-linux-amd64-1.6.18.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":54716969,"digest":"sha256:fc9703afeab6c80d0a74db3d8c97a5385ca5b9e265245290a7fca5b8981b92d6","download_count":24,"created_at":"2026-07-27T20:00:45Z","updated_at":"2026-07-27T20:00:48Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.6.18/ziti-linux-amd64-1.6.18.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/491867851","id":491867851,"node_id":"RA_kwDODVFMN84dUU7L","name":"ziti-linux-arm-1.6.18.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":51178268,"digest":"sha256:9289ab54f511f84bdc10cd07f9f374b8716fcbc65859dbf40fa754ac9ec60286","download_count":3,"created_at":"2026-07-27T20:00:45Z","updated_at":"2026-07-27T20:00:48Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.6.18/ziti-linux-arm-1.6.18.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/491867855","id":491867855,"node_id":"RA_kwDODVFMN84dUU7P","name":"ziti-linux-arm64-1.6.18.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":51226385,"digest":"sha256:fe1e0f70f87c7ba15e784529c894fc6b809dde323c169d665d8af65efb1a191e","download_count":3,"created_at":"2026-07-27T20:00:45Z","updated_at":"2026-07-27T20:00:47Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.6.18/ziti-linux-arm64-1.6.18.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/491867850","id":491867850,"node_id":"RA_kwDODVFMN84dUU7K","name":"ziti-windows-amd64-1.6.18.zip","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/zip","state":"uploaded","size":44224618,"digest":"sha256:8c8c4755004d30b4ceb6b98372c389833d834dcca18f6fe3d7c6815ad49955fd","download_count":16,"created_at":"2026-07-27T20:00:45Z","updated_at":"2026-07-27T20:00:47Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.6.18/ziti-windows-amd64-1.6.18.zip"}],"tarball_url":"https://api.github.com/repos/openziti/ziti/tarball/v1.6.18","zipball_url":"https://api.github.com/repos/openziti/ziti/zipball/v1.6.18","body":"# Release 1.6.18\r\n\r\n## What's New\r\n\r\n* Security fixes (see Security Advisories below)\r\n* Bug fixes and dependency updates\r\n\r\n## Security Advisories\r\n\r\nThis release addresses two control-plane certificate and identity validation vulnerabilities. See the linked\r\nGitHub Security Advisories for full details, impact, and affected versions.\r\n\r\n* [GHSA-mrpr-756c-xm47](https://github.com/openziti/ziti/security/advisories/GHSA-mrpr-756c-xm47) (CVE pending) (Critical) - Improper peer certificate validation on the controller\r\n  cluster mesh, router links, and metrics endpoint. TLS peer checks accepted a connection when any presented\r\n  certificate chained to the trusted CA while taking the peer identity from the leaf certificate, allowing a\r\n  peer to be admitted under a forged identity without possessing a trusted key. On HA/clustered controllers\r\n  this allows joining the controller cluster as an arbitrary controller.\r\n* [GHSA-cc5m-7mhm-xh9f](https://github.com/openziti/ziti/security/advisories/GHSA-cc5m-7mhm-xh9f) (CVE pending) (Medium) - Control-channel connections carrying a channel-type header\r\n  bypassed router certificate and identity verification, allowing an attacker that can reach the controller\r\n  control port to be admitted as an arbitrary router identity and manipulate that router's fabric terminators,\r\n  faults, and circuit routing. Impact is limited to router data model metadata (service and identity names) and\r\n  control-plane manipulation; it does not by itself grant access to the services the network protects.\r\n\r\n## Component Updates and Bug Fixes\r\n\r\n* github.com/openziti/ziti: [v1.6.17 -> v1.6.18](https://github.com/openziti/ziti/compare/v1.6.17...v1.6.18)\r\n    * [Issue #3961](https://github.com/openziti/ziti/issues/3961) - Fix router panic evaluating a process posture check when the client has reported no process/OS posture data\r\n    * [Issue #3868](https://github.com/openziti/ziti/issues/3868) - [Backport-1.6] Tunneler skips iptables rules for services sharing an intercept hostname\r\n    * [Issue #3957](https://github.com/openziti/ziti/issues/3957) - [Backport-1.6] Refcount wildcard-allocated intercept hostnames\r\n    * [Issue #4135](https://github.com/openziti/ziti/issues/4135) - [Backport-1.6] ziti tunnel ignores --dnsSvcIpRange\r\n    * [Issue #4108](https://github.com/openziti/ziti/issues/4108) - Fix controller panic / potential data corruption by copying terminator peer data, instance secret, and eventual event data out of bolt-managed memory\r\n\r\n"},{"url":"https://api.github.com/repos/openziti/ziti/releases/354522509","assets_url":"https://api.github.com/repos/openziti/ziti/releases/354522509/assets","upload_url":"https://uploads.github.com/repos/openziti/ziti/releases/354522509/assets{?name,label}","html_url":"https://github.com/openziti/ziti/releases/tag/v2.0.1","id":354522509,"author":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"node_id":"RE_kwDODVFMN84VIZWN","tag_name":"v2.0.1","target_commitish":"main","name":"v2.0.1","draft":false,"immutable":false,"prerelease":false,"created_at":"2026-07-15T14:55:54Z","updated_at":"2026-07-27T16:47:06Z","published_at":"2026-07-15T15:06:58Z","assets":[{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/478062768","id":478062768,"node_id":"RA_kwDODVFMN84cfqiw","name":"checksums.sha256.txt","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"text/plain; charset=utf-8","state":"uploaded","size":750,"digest":"sha256:7256a0539df54a7891fc14641265f3928417a48c76817b8bbf4fd7a3cb408ca8","download_count":26,"created_at":"2026-07-15T15:06:57Z","updated_at":"2026-07-15T15:06:57Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.1/checksums.sha256.txt"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/478062764","id":478062764,"node_id":"RA_kwDODVFMN84cfqis","name":"sbom-v2.0.1.spdx.json","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/json","state":"uploaded","size":983830,"digest":"sha256:d7a686d5ea895be0b2f59c96e59f8e7faa3aa1eca3acfd7a812c264ac1a535ab","download_count":9,"created_at":"2026-07-15T15:06:56Z","updated_at":"2026-07-15T15:06:57Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.1/sbom-v2.0.1.spdx.json"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/478062765","id":478062765,"node_id":"RA_kwDODVFMN84cfqit","name":"source-v2.0.1.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":4029451,"digest":"sha256:cd9da267981fec7e7a9ba221bd556cbddd2682ae465ed3c97359fc27d4151af2","download_count":9,"created_at":"2026-07-15T15:06:56Z","updated_at":"2026-07-15T15:06:57Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.1/source-v2.0.1.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/478062706","id":478062706,"node_id":"RA_kwDODVFMN84cfqhy","name":"ziti-darwin-amd64-2.0.1.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":55127978,"digest":"sha256:2e395dffa167ad9cdb33170293866dc57200f06875cb51d93a6d621ccaf37e7c","download_count":48,"created_at":"2026-07-15T15:06:54Z","updated_at":"2026-07-15T15:06:56Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.1/ziti-darwin-amd64-2.0.1.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/478062705","id":478062705,"node_id":"RA_kwDODVFMN84cfqhx","name":"ziti-darwin-arm64-2.0.1.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":51348930,"digest":"sha256:a461d235c55bb673215bb0d265228fd85ac210e5aacb00adaa2fcd440e10b5ef","download_count":110,"created_at":"2026-07-15T15:06:54Z","updated_at":"2026-07-15T15:06:56Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.1/ziti-darwin-arm64-2.0.1.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/478062707","id":478062707,"node_id":"RA_kwDODVFMN84cfqhz","name":"ziti-linux-amd64-2.0.1.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":56448688,"digest":"sha256:f4496844d32b78857c1adc83252cd001c842c7f4bd4865fd177d832351de536c","download_count":692,"created_at":"2026-07-15T15:06:54Z","updated_at":"2026-07-15T15:06:57Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.1/ziti-linux-amd64-2.0.1.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/478062704","id":478062704,"node_id":"RA_kwDODVFMN84cfqhw","name":"ziti-linux-arm-2.0.1.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":52822471,"digest":"sha256:9e726d0992935ca146e1c80ad221b908553a70fbca80eb18ad7c8e05aa81556c","download_count":8,"created_at":"2026-07-15T15:06:54Z","updated_at":"2026-07-15T15:06:57Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.1/ziti-linux-arm-2.0.1.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/478062762","id":478062762,"node_id":"RA_kwDODVFMN84cfqiq","name":"ziti-linux-arm64-2.0.1.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":52859427,"digest":"sha256:41d303a6327ec6db42c006f417a7b0859dba3ead263b76dfa75c82f4b06818a2","download_count":193,"created_at":"2026-07-15T15:06:56Z","updated_at":"2026-07-15T15:06:58Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.1/ziti-linux-arm64-2.0.1.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/478062708","id":478062708,"node_id":"RA_kwDODVFMN84cfqh0","name":"ziti-windows-amd64-2.0.1.zip","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/zip","state":"uploaded","size":45623596,"digest":"sha256:ddf3d574cbb7e6ea5c2d16b3317f7d9bec226cf75451c47973d418820dfa104e","download_count":161,"created_at":"2026-07-15T15:06:54Z","updated_at":"2026-07-15T15:06:56Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.1/ziti-windows-amd64-2.0.1.zip"}],"tarball_url":"https://api.github.com/repos/openziti/ziti/tarball/v2.0.1","zipball_url":"https://api.github.com/repos/openziti/ziti/zipball/v2.0.1","body":"# Release 2.0.1\r\n\r\n## What's New\r\n\r\n* Bug fixes\r\n\r\n## Component Updates and Bug Fixes\r\n\r\n* github.com/openziti/ziti/v2: [v2.0.0 -> v2.0.1](https://github.com/openziti/ziti/compare/v2.0.0...v2.0.1)\r\n    * [Issue #4083](https://github.com/openziti/ziti/issues/4083) - [Backport-2.0] Controller bootstrap misclassifies DNS names like `8.8.8.8.nip.io` when generating cert SANs. The advertised address is now classified as IPv4 only on an anchored dotted-quad, and any \":\" is treated as an IPv6 address\r\n    * [Issue #4070](https://github.com/openziti/ziti/issues/4070) - [Backport-2.0] Controller can cache empty apiAddresses forever due to startup race between raft mesh and xweb config load\r\n    * [Issue #4049](https://github.com/openziti/ziti/issues/4049) - [Backport-2.0] Non-admin with enrollment permission can escalate to admin identity\r\n    * [Issue #4031](https://github.com/openziti/ziti/issues/4031) - [Backport-2.0] Router deletes terminators ~12m after creation when host SDK replies with wrong inspect content type\r\n    * [Issue #4054](https://github.com/openziti/ziti/issues/4054) - [Backport-2.0] Router panics evaluating an AnyOf process posture check when client reports no process/OS data\r\n    * [Issue #3959](https://github.com/openziti/ziti/issues/3959) - [Backport-2.0] Refcount wildcard-allocated intercept hostnames\r\n    * [Issue #3958](https://github.com/openziti/ziti/issues/3958) - [Backport-2.0] Tunneler skips iptables rules for services sharing an intercept hostname\r\n    * [Issue #3952](https://github.com/openziti/ziti/issues/3952) - externalIdClaim on CA returns HTTP 500 with empty body for most matcher/parser combinations\r\n    * [Issue #3929](https://github.com/openziti/ziti/issues/3929) - Router does not enforce api-session or identity revocations on live connections\r\n    * [Issue #3928](https://github.com/openziti/ziti/issues/3928) - [Backport-2.0] Router posture-data updates don't revoke SDK-hosted xgress circuits or hosted terminators\r\n\r\n\r\n"},{"url":"https://api.github.com/repos/openziti/ziti/releases/344236845","assets_url":"https://api.github.com/repos/openziti/ziti/releases/344236845/assets","upload_url":"https://uploads.github.com/repos/openziti/ziti/releases/344236845/assets{?name,label}","html_url":"https://github.com/openziti/ziti/releases/tag/v1.5.16","id":344236845,"author":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"node_id":"RE_kwDODVFMN84UhKMt","tag_name":"v1.5.16","target_commitish":"main","name":"v1.5.16","draft":false,"immutable":false,"prerelease":false,"created_at":"2026-06-24T16:17:26Z","updated_at":"2026-06-24T19:13:43Z","published_at":"2026-06-24T16:29:36Z","assets":[{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/456807064","id":456807064,"node_id":"RA_kwDODVFMN84bOlKY","name":"checksums.sha256.txt","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"text/plain; charset=utf-8","state":"uploaded","size":758,"digest":"sha256:ffcddffa65ad52034c1e988b455681da9771702c6e6ab1ef37b44c291f629fa5","download_count":2,"created_at":"2026-06-24T16:29:35Z","updated_at":"2026-06-24T16:29:35Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.5.16/checksums.sha256.txt"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/456807060","id":456807060,"node_id":"RA_kwDODVFMN84bOlKU","name":"sbom-v1.5.16.spdx.json","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/json","state":"uploaded","size":974674,"digest":"sha256:854015354d3c83fae9bee2c331d84a89c51517e8bfa91e7d4f0d6009401ece43","download_count":3,"created_at":"2026-06-24T16:29:35Z","updated_at":"2026-06-24T16:29:36Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.5.16/sbom-v1.5.16.spdx.json"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/456807063","id":456807063,"node_id":"RA_kwDODVFMN84bOlKX","name":"source-v1.5.16.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":3401719,"digest":"sha256:465e80f792eba8ad3483eb44654870c779956cad1a2692e5a841fba8b874ad3e","download_count":3,"created_at":"2026-06-24T16:29:35Z","updated_at":"2026-06-24T16:29:35Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.5.16/source-v1.5.16.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/456807001","id":456807001,"node_id":"RA_kwDODVFMN84bOlJZ","name":"ziti-darwin-amd64-1.5.16.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":50713515,"digest":"sha256:21162e08f8927a3225a652b40a85e3d7fe2a41ca5254af88826fce096cca90dc","download_count":7,"created_at":"2026-06-24T16:29:32Z","updated_at":"2026-06-24T16:29:35Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.5.16/ziti-darwin-amd64-1.5.16.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/456807000","id":456807000,"node_id":"RA_kwDODVFMN84bOlJY","name":"ziti-darwin-arm64-1.5.16.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":47150810,"digest":"sha256:4baf9d8699e97aa4a579bbd6a00b745ac1eff1e83e21960a12029ed311c9dace","download_count":9,"created_at":"2026-06-24T16:29:32Z","updated_at":"2026-06-24T16:29:34Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.5.16/ziti-darwin-arm64-1.5.16.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/456807003","id":456807003,"node_id":"RA_kwDODVFMN84bOlJb","name":"ziti-linux-amd64-1.5.16.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":51945412,"digest":"sha256:b2ed7c363454a8e7270f1e6dd6ef10f1b009770d755a4c9362b9112b0d7a9690","download_count":19,"created_at":"2026-06-24T16:29:32Z","updated_at":"2026-06-24T16:29:35Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.5.16/ziti-linux-amd64-1.5.16.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/456806999","id":456806999,"node_id":"RA_kwDODVFMN84bOlJX","name":"ziti-linux-arm-1.5.16.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":48490870,"digest":"sha256:948376afd875c4d41e23c9a138ef8e7bee9a4302272b63d6fb85f8b8f2bd2b47","download_count":7,"created_at":"2026-06-24T16:29:32Z","updated_at":"2026-06-24T16:29:35Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.5.16/ziti-linux-arm-1.5.16.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/456807054","id":456807054,"node_id":"RA_kwDODVFMN84bOlKO","name":"ziti-linux-arm64-1.5.16.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":48733701,"digest":"sha256:048e60220fe8f8d6105ec2038569f4efaab41c95776dc4df072f6adba6594dcc","download_count":8,"created_at":"2026-06-24T16:29:34Z","updated_at":"2026-06-24T16:29:36Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.5.16/ziti-linux-arm64-1.5.16.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/456807004","id":456807004,"node_id":"RA_kwDODVFMN84bOlJc","name":"ziti-windows-amd64-1.5.16.zip","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/zip","state":"uploaded","size":41769124,"digest":"sha256:79ec876bbb758822aa0816e710af4eaf26c1ab9bafc3bbaad86ecad954b74c61","download_count":14,"created_at":"2026-06-24T16:29:32Z","updated_at":"2026-06-24T16:29:34Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.5.16/ziti-windows-amd64-1.5.16.zip"}],"tarball_url":"https://api.github.com/repos/openziti/ziti/tarball/v1.5.16","zipball_url":"https://api.github.com/repos/openziti/ziti/zipball/v1.5.16","body":"# Release 1.5.16\r\n\r\n## What's New\r\n\r\nUpdate libraries and build with the latest Go version.\r\n\r\n"},{"url":"https://api.github.com/repos/openziti/ziti/releases/331238042","assets_url":"https://api.github.com/repos/openziti/ziti/releases/331238042/assets","upload_url":"https://uploads.github.com/repos/openziti/ziti/releases/331238042/assets{?name,label}","html_url":"https://github.com/openziti/ziti/releases/tag/v1.5.15","id":331238042,"author":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"node_id":"RE_kwDODVFMN84Tvkqa","tag_name":"v1.5.15","target_commitish":"main","name":"v1.5.15","draft":false,"immutable":false,"prerelease":false,"created_at":"2026-05-29T01:51:01Z","updated_at":"2026-05-29T13:38:08Z","published_at":"2026-05-29T02:03:24Z","assets":[{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/432627686","id":432627686,"node_id":"RA_kwDODVFMN84ZyV_m","name":"checksums.sha256.txt","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"text/plain; charset=utf-8","state":"uploaded","size":758,"digest":"sha256:2ad29eea99871d021ece213be79d920286c9bb797fa08b3fc411e03ceb1b41ad","download_count":4,"created_at":"2026-05-29T02:03:21Z","updated_at":"2026-05-29T02:03:21Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.5.15/checksums.sha256.txt"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/432627685","id":432627685,"node_id":"RA_kwDODVFMN84ZyV_l","name":"sbom-v1.5.15.spdx.json","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/json","state":"uploaded","size":988079,"digest":"sha256:073c3ae5467c9b92683731f53b297f3f09571d9cc653ab9916c5339e27554dc2","download_count":2,"created_at":"2026-05-29T02:03:21Z","updated_at":"2026-05-29T02:03:22Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.5.15/sbom-v1.5.15.spdx.json"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/432627687","id":432627687,"node_id":"RA_kwDODVFMN84ZyV_n","name":"source-v1.5.15.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":3401598,"digest":"sha256:b66245c39254bd967b947937d5c8992134f0fc50fa9fe173aed1e8836afcdc7c","download_count":6,"created_at":"2026-05-29T02:03:21Z","updated_at":"2026-05-29T02:03:22Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.5.15/source-v1.5.15.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/432627634","id":432627634,"node_id":"RA_kwDODVFMN84ZyV-y","name":"ziti-darwin-amd64-1.5.15.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":50564771,"digest":"sha256:949df00f3faf096985d97857512494aecf9c93a373ddf31ae44c6b6dd8f086c6","download_count":4,"created_at":"2026-05-29T02:03:18Z","updated_at":"2026-05-29T02:03:21Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.5.15/ziti-darwin-amd64-1.5.15.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/432627633","id":432627633,"node_id":"RA_kwDODVFMN84ZyV-x","name":"ziti-darwin-arm64-1.5.15.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":47006814,"digest":"sha256:b80bffe5111564e9391a1d040ce97cd0149d74d432781cb2a18216857d99f245","download_count":6,"created_at":"2026-05-29T02:03:18Z","updated_at":"2026-05-29T02:03:21Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.5.15/ziti-darwin-arm64-1.5.15.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/432627632","id":432627632,"node_id":"RA_kwDODVFMN84ZyV-w","name":"ziti-linux-amd64-1.5.15.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":51772071,"digest":"sha256:ec0d4f52a8e17de15537f0c43d9a477e65680a76dc0cf7726581f9aa83af5e36","download_count":14,"created_at":"2026-05-29T02:03:18Z","updated_at":"2026-05-29T02:03:21Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.5.15/ziti-linux-amd64-1.5.15.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/432627630","id":432627630,"node_id":"RA_kwDODVFMN84ZyV-u","name":"ziti-linux-arm-1.5.15.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":48367606,"digest":"sha256:b73805573028a63763f15080d32cb1c630e7ece41b9a0561281aa908174ae6ba","download_count":5,"created_at":"2026-05-29T02:03:18Z","updated_at":"2026-05-29T02:03:21Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.5.15/ziti-linux-arm-1.5.15.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/432627681","id":432627681,"node_id":"RA_kwDODVFMN84ZyV_h","name":"ziti-linux-arm64-1.5.15.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":48571037,"digest":"sha256:e7f33700f504fc837c8cf9ece5f11483ad5154066e0979dd875d38d2cd268115","download_count":7,"created_at":"2026-05-29T02:03:21Z","updated_at":"2026-05-29T02:03:24Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.5.15/ziti-linux-arm64-1.5.15.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/432627635","id":432627635,"node_id":"RA_kwDODVFMN84ZyV-z","name":"ziti-windows-amd64-1.5.15.zip","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/zip","state":"uploaded","size":41680356,"digest":"sha256:cd7c9ecc4c8d5b1e32525b637b30b29eb0994a30cc051e20a2aba63bc479cf78","download_count":17,"created_at":"2026-05-29T02:03:18Z","updated_at":"2026-05-29T02:03:20Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.5.15/ziti-windows-amd64-1.5.15.zip"}],"tarball_url":"https://api.github.com/repos/openziti/ziti/tarball/v1.5.15","zipball_url":"https://api.github.com/repos/openziti/ziti/zipball/v1.5.15","body":"# Release 1.5.15\r\n\r\n## What's New\r\n\r\nUpdate libraries and build with the latest Go version.\r\n\r\n"},{"url":"https://api.github.com/repos/openziti/ziti/releases/331205307","assets_url":"https://api.github.com/repos/openziti/ziti/releases/331205307/assets","upload_url":"https://uploads.github.com/repos/openziti/ziti/releases/331205307/assets{?name,label}","html_url":"https://github.com/openziti/ziti/releases/tag/v1.6.17","id":331205307,"author":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"node_id":"RE_kwDODVFMN84Tvcq7","tag_name":"v1.6.17","target_commitish":"main","name":"v1.6.17","draft":false,"immutable":false,"prerelease":false,"created_at":"2026-05-28T23:22:43Z","updated_at":"2026-05-29T01:48:48Z","published_at":"2026-05-28T23:37:32Z","assets":[{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/432528432","id":432528432,"node_id":"RA_kwDODVFMN84Zx9ww","name":"checksums.sha256.txt","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"text/plain; charset=utf-8","state":"uploaded","size":758,"digest":"sha256:52823ddcf6bb9cc8dcf32c2ff2327178cc54c9843f64f211d73815adf4926a52","download_count":7,"created_at":"2026-05-28T23:37:31Z","updated_at":"2026-05-28T23:37:31Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.6.17/checksums.sha256.txt"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/432528425","id":432528425,"node_id":"RA_kwDODVFMN84Zx9wp","name":"sbom-v1.6.17.spdx.json","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/json","state":"uploaded","size":988079,"digest":"sha256:43bd29fa6f715e285a5de6b44bc05b57c1beffeb5ea8a2a41523679373ed3a7d","download_count":3,"created_at":"2026-05-28T23:37:29Z","updated_at":"2026-05-28T23:37:30Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.6.17/sbom-v1.6.17.spdx.json"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/432528428","id":432528428,"node_id":"RA_kwDODVFMN84Zx9ws","name":"source-v1.6.17.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":3509836,"digest":"sha256:0e7220265a490b92ebba805a4dc5e3c105b3555d1a0bbfeed2c587396d61dd7d","download_count":6,"created_at":"2026-05-28T23:37:30Z","updated_at":"2026-05-28T23:37:30Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.6.17/source-v1.6.17.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/432528384","id":432528384,"node_id":"RA_kwDODVFMN84Zx9wA","name":"ziti-darwin-amd64-1.6.17.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":53043113,"digest":"sha256:cc58422b1f3da68cb3aac0ff9214ea823aa0c17db40c10a7f7d44443890bed5e","download_count":167,"created_at":"2026-05-28T23:37:24Z","updated_at":"2026-05-28T23:37:31Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.6.17/ziti-darwin-amd64-1.6.17.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/432528421","id":432528421,"node_id":"RA_kwDODVFMN84Zx9wl","name":"ziti-darwin-arm64-1.6.17.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":49379795,"digest":"sha256:8b6444fae7be6cfc4f2b84140e92454d49c0b0bc05167673cc112458fc64d17f","download_count":338,"created_at":"2026-05-28T23:37:29Z","updated_at":"2026-05-28T23:37:31Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.6.17/ziti-darwin-arm64-1.6.17.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/432528387","id":432528387,"node_id":"RA_kwDODVFMN84Zx9wD","name":"ziti-linux-amd64-1.6.17.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":54292606,"digest":"sha256:3d8955bfe7f92fc60aeb18ca585f89119f724e47b38e463cfe08be2cb4940e9e","download_count":426,"created_at":"2026-05-28T23:37:24Z","updated_at":"2026-05-28T23:37:31Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.6.17/ziti-linux-amd64-1.6.17.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/432528385","id":432528385,"node_id":"RA_kwDODVFMN84Zx9wB","name":"ziti-linux-arm-1.6.17.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":50783276,"digest":"sha256:4a557e2042fec3498ea9e982c7e74d346cfe69e9bcf69e21494b68174d8a13d5","download_count":6,"created_at":"2026-05-28T23:37:24Z","updated_at":"2026-05-28T23:37:29Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.6.17/ziti-linux-arm-1.6.17.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/432528386","id":432528386,"node_id":"RA_kwDODVFMN84Zx9wC","name":"ziti-linux-arm64-1.6.17.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":50836729,"digest":"sha256:e53d504c712582ad2a21fe7390ef8c5960d96b2eecfa8ac7c1045b3c512db3c8","download_count":12,"created_at":"2026-05-28T23:37:24Z","updated_at":"2026-05-28T23:37:30Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.6.17/ziti-linux-arm64-1.6.17.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/432528388","id":432528388,"node_id":"RA_kwDODVFMN84Zx9wE","name":"ziti-windows-amd64-1.6.17.zip","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/zip","state":"uploaded","size":43857748,"digest":"sha256:a51de0fdb5f8b26694c3c840e8a82f34f73a39cfddfb6e5a5738e88498190d1f","download_count":501,"created_at":"2026-05-28T23:37:24Z","updated_at":"2026-05-28T23:37:28Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.6.17/ziti-windows-amd64-1.6.17.zip"}],"tarball_url":"https://api.github.com/repos/openziti/ziti/tarball/v1.6.17","zipball_url":"https://api.github.com/repos/openziti/ziti/zipball/v1.6.17","body":"# Release 1.6.17\r\n\r\n## What's New\r\n\r\n* Package publishing fixes\r\n\r\n"},{"url":"https://api.github.com/repos/openziti/ziti/releases/330530910","assets_url":"https://api.github.com/repos/openziti/ziti/releases/330530910/assets","upload_url":"https://uploads.github.com/repos/openziti/ziti/releases/330530910/assets{?name,label}","html_url":"https://github.com/openziti/ziti/releases/tag/v1.6.16","id":330530910,"author":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"node_id":"RE_kwDODVFMN84Ts4Be","tag_name":"v1.6.16","target_commitish":"main","name":"v1.6.16","draft":false,"immutable":false,"prerelease":false,"created_at":"2026-05-27T21:29:07Z","updated_at":"2026-05-28T14:13:38Z","published_at":"2026-05-27T21:40:46Z","assets":[{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/431456928","id":431456928,"node_id":"RA_kwDODVFMN84Zt4Kg","name":"checksums.sha256.txt","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"text/plain; charset=utf-8","state":"uploaded","size":758,"digest":"sha256:769436bc4d3ed865a859c06a486396be5af50bb7f7133262d2877018b40cbd0a","download_count":5,"created_at":"2026-05-27T21:40:45Z","updated_at":"2026-05-27T21:40:45Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.6.16/checksums.sha256.txt"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/431456926","id":431456926,"node_id":"RA_kwDODVFMN84Zt4Ke","name":"sbom-v1.6.16.spdx.json","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/json","state":"uploaded","size":988079,"digest":"sha256:49c5b2c10e7e45d8df080676f6c559ddb6868cba367663c1fbb66fefd3a783dd","download_count":3,"created_at":"2026-05-27T21:40:44Z","updated_at":"2026-05-27T21:40:45Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.6.16/sbom-v1.6.16.spdx.json"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/431456927","id":431456927,"node_id":"RA_kwDODVFMN84Zt4Kf","name":"source-v1.6.16.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":3509775,"digest":"sha256:d3165b3a3662ad85856a3a3a51011953bd78090b448f8bc4728b58d7bbd9f053","download_count":6,"created_at":"2026-05-27T21:40:44Z","updated_at":"2026-05-27T21:40:45Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.6.16/source-v1.6.16.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/431456903","id":431456903,"node_id":"RA_kwDODVFMN84Zt4KH","name":"ziti-darwin-amd64-1.6.16.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":53043104,"digest":"sha256:f9a3462d90a2cce42216754ce61ce56df3d82b74166164986629ffef8d63786a","download_count":9,"created_at":"2026-05-27T21:40:41Z","updated_at":"2026-05-27T21:40:44Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.6.16/ziti-darwin-amd64-1.6.16.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/431456922","id":431456922,"node_id":"RA_kwDODVFMN84Zt4Ka","name":"ziti-darwin-arm64-1.6.16.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":49379825,"digest":"sha256:0edcad0c9794f4fb31084e24627e9bc993fd6a6e6ee26ca56d81eb2b0353ca84","download_count":8,"created_at":"2026-05-27T21:40:44Z","updated_at":"2026-05-27T21:40:46Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.6.16/ziti-darwin-arm64-1.6.16.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/431456904","id":431456904,"node_id":"RA_kwDODVFMN84Zt4KI","name":"ziti-linux-amd64-1.6.16.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":54292577,"digest":"sha256:e0c794cb93342e4b1e7c8a5e7e657918a49bf0453704bb8975c424897a246671","download_count":53,"created_at":"2026-05-27T21:40:41Z","updated_at":"2026-05-27T21:40:44Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.6.16/ziti-linux-amd64-1.6.16.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/431456906","id":431456906,"node_id":"RA_kwDODVFMN84Zt4KK","name":"ziti-linux-arm-1.6.16.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":50783271,"digest":"sha256:0504ac67229f5290772452a2d82eb61e1c3bdd2b48fbf2dbf2601727ca250e83","download_count":8,"created_at":"2026-05-27T21:40:41Z","updated_at":"2026-05-27T21:40:44Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.6.16/ziti-linux-arm-1.6.16.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/431456905","id":431456905,"node_id":"RA_kwDODVFMN84Zt4KJ","name":"ziti-linux-arm64-1.6.16.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":50836739,"digest":"sha256:2d340a767087ae7a9383c9520ea0dfa7d12fc0c795743a7ca40c162c125b1cee","download_count":8,"created_at":"2026-05-27T21:40:41Z","updated_at":"2026-05-27T21:40:44Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.6.16/ziti-linux-arm64-1.6.16.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/431456907","id":431456907,"node_id":"RA_kwDODVFMN84Zt4KL","name":"ziti-windows-amd64-1.6.16.zip","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/zip","state":"uploaded","size":43857763,"digest":"sha256:5fcb4e1a6552022729acf029dd1be1df08e4d0c7e2cb51cb157a754275ac957b","download_count":14,"created_at":"2026-05-27T21:40:41Z","updated_at":"2026-05-27T21:40:43Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.6.16/ziti-windows-amd64-1.6.16.zip"}],"tarball_url":"https://api.github.com/repos/openziti/ziti/tarball/v1.6.16","zipball_url":"https://api.github.com/repos/openziti/ziti/zipball/v1.6.16","body":"# Release 1.6.16\r\n\r\n## What's New\r\n\r\n* Bug fixes and dependency updates\r\n\r\n## Component Updates and Bug Fixes\r\n\r\n* github.com/openziti/channel/v4: [v4.2.35 -> v4.3.11](https://github.com/openziti/channel/compare/v4.2.35...v4.3.11)\r\n    * [Issue #242](https://github.com/openziti/channel/issues/242) - Reconnecting channel shouldn't allow changing ids\r\n    * [Issue #235](https://github.com/openziti/channel/issues/235) - Bump allowed hello message headers size to 16k from 4k\r\n    * [Issue #228](https://github.com/openziti/channel/issues/228) - Ensure that Underlay never return nil on MultiChannel\r\n    * [Issue #226](https://github.com/openziti/channel/issues/226) - Allow specifying a minimum number of underlays for a channel, regardless of underlay type\r\n    * [Issue #225](https://github.com/openziti/channel/issues/225) - Add ChannelCreated to the UnderlayHandler API to allow handlers to be initialized with the channel before binding\r\n    * [Issue #224](https://github.com/openziti/channel/issues/224) - Update the underlay dispatcher to allow unknown underlay types to fall through to the default\r\n    * [Issue #222](https://github.com/openziti/channel/issues/222) - Allow injecting the underlay type into messages\r\n\r\n* github.com/openziti/edge-api: [v0.26.47 -> v0.27.5](https://github.com/openziti/edge-api/compare/v0.26.47...v0.27.5)\r\n    * [Issue #175](https://github.com/openziti/edge-api/issues/175) - ctrlChanListeners should have x-omit-empty: false attribute\r\n    * [Issue #170](https://github.com/openziti/edge-api/issues/170) - Add preferredLeader flag to controllers\r\n    * [Issue #167](https://github.com/openziti/edge-api/issues/167) - Add ctrlChanListeners to router types\r\n    * [Issue #164](https://github.com/openziti/edge-api/issues/164) - Add permissions list to identity\r\n\r\n* github.com/openziti/foundation/v2: [v2.0.77 -> v2.0.90](https://github.com/openziti/foundation/compare/v2.0.77...v2.0.90)\r\n    * [Issue #472](https://github.com/openziti/foundation/issues/472) - Add support for multi-bit set/get to AtomicBitSet\r\n    * [Issue #464](https://github.com/openziti/foundation/issues/464) - Add support for -pre in versions\r\n\r\n* github.com/openziti/identity: [v1.0.116 -> v1.0.128](https://github.com/openziti/identity/compare/v1.0.116...v1.0.128)\r\n* github.com/openziti/metrics: [v1.4.3 -> v1.4.5](https://github.com/openziti/metrics/compare/v1.4.3...v1.4.5)\r\n    * [Issue #58](https://github.com/openziti/metrics/issues/58) - Add GaugeFloat64 support\r\n\r\n* github.com/openziti/sdk-golang: [v1.2.4-patch1 -> v1.6.0](https://github.com/openziti/sdk-golang/compare/v1.2.4-patch1...v1.6.0)\r\n    * [Issue #895](https://github.com/openziti/sdk-golang/issues/895) - Limit effect sudden rtt spikes can have on rtt moving average\r\n    * [Issue #902](https://github.com/openziti/sdk-golang/issues/902) - Inspect response message content types are mixed up\r\n    * [Issue #887](https://github.com/openziti/sdk-golang/issues/887) - Fix listener manager cleanup\r\n    * [Issue #886](https://github.com/openziti/sdk-golang/issues/886) - When controller is busy during service refresh, backoff and retry instead of falling back to full refresh\r\n    * [Issue #885](https://github.com/openziti/sdk-golang/issues/885) - Only compare relevant service fields when looking for changes\r\n    * [Issue #884](https://github.com/openziti/sdk-golang/issues/884) - Add deadline for bind establishment\r\n    * [Issue #883](https://github.com/openziti/sdk-golang/issues/883) - Router level listener can be left open if multi-listener closes during listener establishment\r\n    * [Issue #832](https://github.com/openziti/sdk-golang/issues/832) - Fuzz session refresh timers\r\n    * [Issue #879](https://github.com/openziti/sdk-golang/issues/879) - Return the connId in inspect response\r\n    * [Issue #878](https://github.com/openziti/sdk-golang/issues/878) - Fix responses from rx goroutines\r\n    * [Issue #874](https://github.com/openziti/sdk-golang/issues/874) - Add inspect support at the context level\r\n    * [Issue #871](https://github.com/openziti/sdk-golang/issues/871) - Make SDK better at sticking to MaxTerminator terminators\r\n    * [Issue #708](https://github.com/openziti/sdk-golang/issues/708) - Support for Go's built-in context in Dial methods\r\n    * [Issue #860](https://github.com/openziti/sdk-golang/issues/860) - Make the dialing identity's id and name available on dialed connections\r\n    * [Issue #857](https://github.com/openziti/sdk-golang/issues/857) - Use new error code and retry hints to correctly react to terminator errors\r\n    * [Issue #847](https://github.com/openziti/sdk-golang/issues/847) - Ensure the initial version check succeeds, to ensure we don't legacy sessions on ha or oidc-enabled controllers\r\n    * [Issue #824](https://github.com/openziti/sdk-golang/pull/824) - release notes and hard errors on no TOTP handler breaks partial auth events\r\n    * [Issue #818](https://github.com/openziti/sdk-golang/issues/818) - Full re-auth should not clear services list, as that breaks the on-change logic\r\n    * [Issue #817](https://github.com/openziti/sdk-golang/issues/817) - goroutines can get stuck when iterating over randomized HA controller list\r\n    * [Issue #736](https://github.com/openziti/sdk-golang/issues/736) - Migrate from github.com/mailru/easyjson\r\n    * [Issue #813](https://github.com/openziti/sdk-golang/issues/813) - SDK doesn't stop close listener when it detects that a service being hosted gets deleted\r\n    * [Issue #811](https://github.com/openziti/sdk-golang/issues/811) - Credentials are lost when explicitly set\r\n    * [Issue #807](https://github.com/openziti/sdk-golang/issues/807) - Don't send close from rxer to avoid blocking\r\n\r\n* github.com/openziti/secretstream: [v0.1.39 -> v0.1.49](https://github.com/openziti/secretstream/compare/v0.1.39...v0.1.49)\r\n* github.com/openziti/transport/v2: [v2.0.193 -> v2.0.215](https://github.com/openziti/transport/compare/v2.0.193...v2.0.215)\r\n    * [Issue #31](https://github.com/openziti/transport/issues/31) - ipv6 Transport Address Parsing\r\n    * [Issue #149](https://github.com/openziti/transport/issues/149) - Archive transwarp code\r\n\r\n* github.com/openziti/ziti: [v1.6.15 -> v1.6.16](https://github.com/openziti/ziti/compare/v1.6.15...v1.6.16)\r\n    * [Issue #3788](https://github.com/openziti/ziti/issues/3788) - OIDC Endpoints return 400 Bad Request instead of underlying error\r\n    * [Issue #3781](https://github.com/openziti/ziti/issues/3781) - [Backport-1.6] ER/T half-close logic is incorrect\r\n\r\n\r\n"},{"url":"https://api.github.com/repos/openziti/ziti/releases/326144067","assets_url":"https://api.github.com/repos/openziti/ziti/releases/326144067/assets","upload_url":"https://uploads.github.com/repos/openziti/ziti/releases/326144067/assets{?name,label}","html_url":"https://github.com/openziti/ziti/releases/tag/v2.0.0","id":326144067,"author":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"node_id":"RE_kwDODVFMN84TcJBD","tag_name":"v2.0.0","target_commitish":"main","name":"v2.0.0","draft":false,"immutable":false,"prerelease":false,"created_at":"2026-05-20T17:47:12Z","updated_at":"2026-05-20T18:38:12Z","published_at":"2026-05-20T17:54:21Z","assets":[{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/425433519","id":425433519,"node_id":"RA_kwDODVFMN84ZW5mv","name":"checksums.sha256.txt","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"text/plain; charset=utf-8","state":"uploaded","size":750,"digest":"sha256:1442e97c941503e08b7a8df6a339bea2403b596b23c133258fcb3d3c343742fb","download_count":149,"created_at":"2026-05-20T17:54:19Z","updated_at":"2026-05-20T17:54:19Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0/checksums.sha256.txt"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/425433514","id":425433514,"node_id":"RA_kwDODVFMN84ZW5mq","name":"sbom-v2.0.0.spdx.json","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/json","state":"uploaded","size":988218,"digest":"sha256:503deca338509036822500196afce728aa574d9723c83798e4854a27ede21937","download_count":17,"created_at":"2026-05-20T17:54:19Z","updated_at":"2026-05-20T17:54:19Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0/sbom-v2.0.0.spdx.json"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/425433517","id":425433517,"node_id":"RA_kwDODVFMN84ZW5mt","name":"source-v2.0.0.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":4004799,"digest":"sha256:2fc80ce108b7c45aa526d75b79a061c0e63a166425b7f9745def701ff649c85e","download_count":53,"created_at":"2026-05-20T17:54:19Z","updated_at":"2026-05-20T17:54:20Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0/source-v2.0.0.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/425433429","id":425433429,"node_id":"RA_kwDODVFMN84ZW5lV","name":"ziti-darwin-amd64-2.0.0.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":54798240,"digest":"sha256:f8c46a545cf115b4bf3518361f3bd5733b7b5514b66f401257b5732316f662c6","download_count":324,"created_at":"2026-05-20T17:54:14Z","updated_at":"2026-05-20T17:54:19Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0/ziti-darwin-amd64-2.0.0.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/425433432","id":425433432,"node_id":"RA_kwDODVFMN84ZW5lY","name":"ziti-darwin-arm64-2.0.0.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":51060129,"digest":"sha256:7d7b99419ab3a1ee6a395808ccd1f9fc23569a372e2af060a95e9afd02e0279a","download_count":609,"created_at":"2026-05-20T17:54:14Z","updated_at":"2026-05-20T17:54:18Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0/ziti-darwin-arm64-2.0.0.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/425433427","id":425433427,"node_id":"RA_kwDODVFMN84ZW5lT","name":"ziti-linux-amd64-2.0.0.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":56113536,"digest":"sha256:0cd678b420efc175421573fc0164b273cc484b017a453d3ac5966f0b5294313e","download_count":6098,"created_at":"2026-05-20T17:54:14Z","updated_at":"2026-05-20T17:54:18Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0/ziti-linux-amd64-2.0.0.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/425433511","id":425433511,"node_id":"RA_kwDODVFMN84ZW5mn","name":"ziti-linux-arm-2.0.0.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":52509869,"digest":"sha256:6fed9930efd22039a3e4fc185f86c6dcb5a7e687c5a47a35a850b70b8e7c18e5","download_count":13,"created_at":"2026-05-20T17:54:18Z","updated_at":"2026-05-20T17:54:21Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0/ziti-linux-arm-2.0.0.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/425433431","id":425433431,"node_id":"RA_kwDODVFMN84ZW5lX","name":"ziti-linux-arm64-2.0.0.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":52546495,"digest":"sha256:810cb1207092f93992aa0d09327e87ec40f623a8e16da5582562aba66c0d17fb","download_count":6721,"created_at":"2026-05-20T17:54:14Z","updated_at":"2026-05-20T17:54:19Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0/ziti-linux-arm64-2.0.0.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/425433430","id":425433430,"node_id":"RA_kwDODVFMN84ZW5lW","name":"ziti-windows-amd64-2.0.0.zip","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/zip","state":"uploaded","size":45357778,"digest":"sha256:53f0e42b33fd4e46bf749f704109199fbdf5b5dd0266f3b91302c8a5194cdb9a","download_count":1032,"created_at":"2026-05-20T17:54:14Z","updated_at":"2026-05-20T17:54:18Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0/ziti-windows-amd64-2.0.0.zip"}],"tarball_url":"https://api.github.com/repos/openziti/ziti/tarball/v2.0.0","zipball_url":"https://api.github.com/repos/openziti/ziti/zipball/v2.0.0","body":"# Release 2.0.0\r\n\r\n## What's New\r\n\r\nThis is the next major version release of OpenZiti, following the 1.0 release in April 2024.\r\nOf particular note is that HA controllers are now considered ready for general use.\r\nThis release also introduces a new permissions model, OIDC/JWT token-based enrollment, \r\nclustering performance improvements, and a number of other features and fixes. Because \r\nsome of these changes are not backwards compatible with older routers, we're marking this \r\nas a major version bump.\r\n\r\n### HA Controllers are now considered ready for general use\r\n\r\nThis is a pretty big milestone and marks the completion of work that's been ongoing for a couple of years.\r\nThe HA work has brought with it some notable changes. Authentication now uses JWTs by default. Using JWTs\r\nmeans the controllers don't need to store session and propagate them to the routers. This removes a bottleneck\r\nfrom the network and allows the load to be more easily distributed among controllers and routers.\r\n\r\nTo support distributed authentication, the routers now get a bespoke version of the data model. In addition\r\nto enabling distributed authentication this will allow us to remove the need for service polling and further \r\nreduce the load on controllers in the future.\r\n\r\n### Router Compatibility\r\n\r\nRelated to the JWT work, routers with version 2.+ will only work with controllers that are version 2.+. This means\r\nto upgrade your network, controllers should be upgraded first. Routers can then be upgraded individually.\r\n\r\n2.x routers should still work fine with older router versions.\r\n\r\nWe try very hard to avoid breaking changes like this, but sometimes the engineering trade-offs lead there. This change\r\nwas first made in the 1.7 release. That release has not been marked stable, and we have no plans to do so, because\r\nof the backwards incompatibility. \r\n\r\nIf you need to support in the 1.6.12+ range, see the section \"OIDC enabled by default\".\r\n\r\n### Release Policy and LTS\r\n\r\nAlongside this release we're publishing a formal [Release Policy](./RELEASE_POLICY.md). The goal is to give\r\noperators a predictable upgrade cadence and a clear answer to \"is my version still supported?\"\r\n\r\nThe short version:\r\n\r\n* **2.0 is the current LTS (N).** It will receive security fixes and critical bug fixes.\r\n* **1.6 is now the maintenance LTS (N-1).** It will receive security fixes and fixes for critical production\r\n  defects only.\r\n* 2.0 remains the current LTS until the next LTS is cut, approximately a year from now. At that point 2.0 will\r\n  move to maintenance and **1.6 will reach end of life and no longer be supported**.\r\n* Two active LTS versions at any time means you're always within a two-year support window.\r\n\r\nA few things worth calling out from the policy that operators planning upgrades should know:\r\n\r\n* Sequential upgrades (N-1 → N) are the only tested and guaranteed upgrade path. Skipping LTS generations\r\n  carries no compatibility guarantee, so 1.6 users should plan their move to 2.0 before 1.6 goes EOL.\r\n* The latest released versions of all supported SDKs and tunnelers (Go, C, Java, Swift, Python, Node.js,\r\n  .NET/C#, Desktop Edge, mobile clients) are guaranteed to work against any active LTS controller/router.\r\n  The SDK/tunneler versions current at the time an LTS was cut also remain compatible with subsequent patch\r\n  releases of that same LTS, so deployments that pin their SDK won't be broken by a controller/router patch\r\n  within the same LTS generation.\r\n* Releases between LTS cuts (the \"Latest Development\" track) continue to ship features and fixes, but carry\r\n  no LTS guarantees.\r\n* Feature backports to LTS are exceptional and require explicit maintainer approval. They are never applied\r\n  to the maintenance LTS.\r\n\r\nSee [RELEASE_POLICY.md](./RELEASE_POLICY.md) for the full lifecycle, support scope per phase, testing\r\nguarantees, and version compatibility details.\r\n\r\n### New Permissions Model (BETA)\r\n\r\nAs one feature goes out of beta, another arrives into beta. This release introduces a new permissions system\r\nfor more fine grained control to the management API. It's not expected to change, but may do so based on feedback\r\nfrom users.\r\n\r\n### CLI Reorganization\r\n\r\nThe `ziti` CLI has been reorganized to consolidate commands that were previously \r\nspread across `ziti edge` and `ziti fabric` into unified top-level commands. Entity \r\nmanagement is now available directly via `ziti create`, `ziti delete`, `ziti list`, \r\nand `ziti update`. Session management has been simplified with top-level `ziti login`. \r\nThe existing `ziti edge` and `ziti fabric` command trees remain available.\r\n\r\n**Breaking change:** `ziti create ca` now creates a Ziti edge Certificate Authority \r\n(previously it created a PKI CA). PKI CA creation is still available via `ziti pki create ca`.\r\n\r\nSee [CLI Reorganization Details](#cli-reorganization-details) for the full command mapping.\r\n\r\n### Updated Release Process\r\n\r\nWe have moved to creating `-preN` releases for major and minor versions. This way we can put out release candidates,\r\nor feature previews and put them through internal testing and let interested folks from the community try them out.\r\nThen, when we're ready, we can run the full validation suite against the last pre-release and retag it. \r\n\r\nPatch releases won't have `-preN` and should contain only high priority bug fixes.\r\n\r\n### Deprecation Cleanup\r\n\r\nSince we already have a breaking change, we're removing some other backwards compatibility code.\r\n\r\n* Controller managed links \r\n    * Router managed links were introduced in v0.30.0. \r\n    * If you're upgrading from an older versions, you'll want to upgrade to the latest 1.x release before jumping to 2.x\r\n    * Github tracking issue: https://github.com/openziti/ziti/issues/3512\r\n* `ziti edge create identity <type>`\r\n    * Identity types other than router were removed in v0.30.2\r\n    * The `type` can be dropped from the CLI command\r\n    * Github tracking issue: https://github.com/openziti/ziti/issues/3532\r\n* Terminator create/update/delete events\r\n    * These have been superseded by entity change events, which also have create/update/delete events for terminators\r\n    * Entity change events were introduced in v0.28.0\r\n    * Github tracking issue: https://github.com/openziti/ziti/issues/3531\r\n* `xgress_edge_tunnel` v1\r\n    * This is the first implementation of the tunneler in edge-router code (ER/T) which used legacy api sessions and services\r\n    * The v2 version uses the router data model and was introduced in v0.30.x\r\n    * Github tracking issue: https://github.com/openziti/ziti/issues/3516\r\n* Service policy filter `type = 1` / `type = 2`\r\n    * Service policy list queries now expect the string form (`type = \"Dial\"`, `type = \"Bind\"`) matching the REST API\r\n    * The integer form was an undocumented side effect of the internal storage format and never worked with the documented filter names\r\n    * Github tracking issue: https://github.com/openziti/ziti/issues/3818\r\n\r\n### Generic SPA Hosting\r\n\r\nThe controller's web layer now supports hosting arbitrary single-page applications via a generic\r\n`binding: spa` API. Each `binding: spa` entry takes a `path` (which becomes the URL context root) and\r\na `location` (the directory to serve). Multiple SPAs can be registered side by side at different\r\ncontext roots. Example:\r\n\r\n```yaml\r\n- binding: spa\r\n  options:\r\n    path: zac\r\n    location: /opt/openziti/share/console\r\n    indexFile: index.html\r\n- binding: spa\r\n  options:\r\n    path: my-app\r\n    location: /opt/my-app\r\n    indexFile: index.html\r\n```\r\n\r\nThe previous `binding: zac` is preserved as a back-compat shim and continues to work without\r\nmodification, but emits a deprecation warning at startup. New deployments should prefer\r\n`binding: spa` with an explicit `path`. The legacy shim retains a global `/assets/*` URL capture so\r\nZAC bundles built with absolute asset paths keep working; new SPAs bound via `binding: spa` are\r\nexpected to use relative URLs (or be built with `<base href>` matching their `path`).\r\n\r\nThe SPA file-serving handler also gained defense-in-depth path-traversal checks (boundary-aware\r\nprefix matching plus a `filepath.Rel`-based containment check on every served file) so that a\r\ncrafted URL cannot escape the configured `location` even if the standard library's own protections\r\never change.\r\n\r\n### Legacy Session Deprecation\r\n\r\nOIDC sessions are now preferred. They are the default, or will become the default for SDKs and tunnelers. They are also required\r\nwhen running HA. Legacy API and service session are now deprecated and will be removed in the OpenZiti v3.0.0 release. \r\n\r\n### Additional Features\r\n\r\n* Controllers can now optionally bind APIs using an OpenZiti identity\r\n* `ziti edge login` now supports the `--network-identity` flag to authenticate and establish connections through the Ziti overlay network\r\n* `ziti edge login` now supports using a bearer token with `--token` for authentication. The token is expected to be\r\n  provided as just the JWT, not with the \"Bearer \" prefix\r\n* Identity configuration can now be loaded from files or environment variables for flexible deployment scenarios\r\n* Identities can now be provisioned just-in-time through OIDC/JWT token-based enrollment\r\n* Multiple model updates can now be in-flight at the same time, improving clustering performance\r\n* Authentication-related model updates can now be non-blocking and even dropped if the system is too busy\r\n* Routers now provide more error context to SDKs for terminator errors, enabling better retry behavior\r\n* New `proxy.v1` config type for dynamic service proxies (originally released in 1.7.0)\r\n* New alert event type for surfacing operational issues to network operators - Beta (originally released in 1.7.0)\r\n* New Azure Service Bus event sink for streaming controller events, contributed by @ffaraone (originally released in 1.7.0)\r\n* Bundled ZAC upgraded to 4.0\r\n* Build updated to Go 1.25\r\n* CLI cleaned up to remove calls to `os.Exit`, making it more friendly for embedding\r\n* OIDC is now enabled by default\r\n* Controller Edge APIs now return `WWW-Authenticate` response headers on `401 Unauthorized` responses, giving clients actionable information about which auth methods are accepted and what went wrong\r\n* HA Controllers can be marked as 'preferredLeader' via config\r\n* Dynamic cost range for smart routing expanded beyond the previous 64K limit\r\n* Dial failures now return the circuit ID and error information for easier debugging\r\n* Router-to-controller control channels now support multiple underlays with priority-based message routing\r\n* The dialing identity's ID and name are now forwarded to the hosting SDK\r\n* Controllers can now dial routers to establish control channels, enabling connectivity when routers are behind firewalls (Beta)\r\n* Refresh-token revocations are now batched and best-effort, removing the database/raft bottleneck on token refreshes\r\n* [OIDC discovery endpoint extensions](#oidc-discovery-endpoint-extensions) - OpenZiti-specific endpoint URLs in the OIDC discovery document\r\n* `ziti edge quickstart` now always runs in HA mode. The `ha` subcommand has been removed. Use\r\n  `ziti edge quickstart join` to add additional members to the cluster. Note: existing quickstart instances\r\n  are not compatible with the new HA-only mode and will need to be recreated.\r\n* The `--clustered` flag on `ziti create config controller` has been removed; the generated config is always\r\n  cluster-ready. If you have scripts passing `--clustered`, remove it.\r\n* [Connect events pool](#connect-events-pool) - fixes a goroutine leak when routers reconnect and ensures per-router event ordering\r\n* [Multiple DNS upstreams](#multiple-dns-upstreams) - the tunneler can now fan out recursive queries to several upstream resolvers in parallel\r\n* [OIDC CSR authentication](#oidc-csr-authentication) - identities can submit a CSR during OIDC authentication to obtain a session-bound certificate for mTLS channel communication\r\n\r\n## OIDC Discovery Endpoint Extensions\r\n\r\nThe OIDC discovery document (`/.well-known/openid-configuration`) now includes a vendor-specific\r\n`openziti_endpoints` field. This lets SDKs discover OpenZiti's custom login and MFA endpoints at\r\nruntime instead of hardcoding paths.\r\n\r\nThe field contains absolute URLs for each endpoint, derived from the issuer the client connected to:\r\n\r\n```json\r\n{\r\n  \"issuer\": \"https://controller.example.com:1280/oidc\",\r\n  \"authorization_endpoint\": \"https://controller.example.com:1280/oidc/authorize\",\r\n  \"token_endpoint\": \"https://controller.example.com:1280/oidc/oauth/token\",\r\n  \"...other standard OIDC fields...\",\r\n  \"openziti_endpoints\": {\r\n    \"password\":           \"https://controller.example.com:1280/oidc/login/password\",\r\n    \"cert\":               \"https://controller.example.com:1280/oidc/login/cert\",\r\n    \"ext_jwt\":            \"https://controller.example.com:1280/oidc/login/ext-jwt\",\r\n    \"totp\":               \"https://controller.example.com:1280/oidc/login/totp\",\r\n    \"totp_enroll\":        \"https://controller.example.com:1280/oidc/login/totp/enroll\",\r\n    \"totp_enroll_verify\": \"https://controller.example.com:1280/oidc/login/totp/enroll/verify\",\r\n    \"auth_queries\":       \"https://controller.example.com:1280/oidc/login/auth-queries\"\r\n  }\r\n}\r\n```\r\n\r\n| Key                | Method(s)   | Description                                       |\r\n|--------------------|-------------|---------------------------------------------------|\r\n| `password`         | POST        | Username/password authentication                  |\r\n| `cert`             | POST        | Client certificate authentication                 |\r\n| `ext_jwt`          | POST        | External JWT authentication                       |\r\n| `totp`             | POST        | TOTP code verification for MFA                    |\r\n| `totp_enroll`      | POST/DELETE | Start (POST) or delete (DELETE) TOTP enrollment   |\r\n| `totp_enroll_verify`| POST       | Verify a TOTP enrollment code                     |\r\n| `auth_queries`     | GET         | Retrieve pending authentication queries           |\r\n\r\nWhen the controller serves edge-oidc on multiple web servers, each discovery response reflects\r\nthe issuer (and port) the client connected to.\r\n\r\n## OIDC CSR Authentication\r\n\r\nIdentities that authenticate via non-certificate methods (password, external JWT) can now submit\r\na CSR during OIDC authentication to obtain a session-bound certificate. This enables mTLS channel\r\ncommunication with edge routers for identities that would otherwise have no client certificate.\r\n\r\nCertificate-authenticated identities can also submit a CSR to obtain an additional session\r\ncertificate alongside their authenticating certificate.\r\n\r\n### How It Works\r\n\r\nA CSR is submitted as part of the OIDC login credentials. The controller signs it and returns\r\nthe certificate PEM as a `session_cert` field in the token endpoint JSON response:\r\n\r\n```json\r\n{\r\n  \"access_token\": \"eyJ...\",\r\n  \"token_type\": \"bearer\",\r\n  \"refresh_token\": \"eyJ...\",\r\n  \"id_token\": \"eyJ...\",\r\n  \"expires_in\": 1800,\r\n  \"session_cert\": \"-----BEGIN CERTIFICATE-----\\nMII...\"\r\n}\r\n```\r\n\r\nThe issued certificate contains a SPIFFE ID binding it to the identity and API session:\r\n```\r\nspiffe://{trustDomain}/identity/{identityId}/apiSession/{apiSessionId}/apiSessionCertificate/{certId}\r\n```\r\n\r\n### CSR Submission Points\r\n\r\n| Token Endpoint Grant Type | CSR Source                            | Use Case             |\r\n|---------------------------|---------------------------------------|----------------------|\r\n| Authorization Code        | `csrPem` field in login POST body     | Initial cert issue   |\r\n| Refresh Token             | `csr_pem` form parameter              | Cert rotation        |\r\n| Token Exchange            | `csr_pem` form parameter              | Cert rotation        |\r\n\r\n### Certificate Fingerprint Claims\r\n\r\nThe access token JWT includes two related claims:\r\n\r\n- `z_cfs` (CertFingerprints): all certificate fingerprints valid for this session. Contains the\r\n  authenticating cert fingerprint (for cert auth) and/or the CSR-issued session cert fingerprint.\r\n- `z_acf` (AuthCertFingerprint): the authenticating certificate fingerprint, present only for\r\n  certificate-authenticated sessions.\r\n\r\nOn cert rotation via refresh or token exchange, `z_cfs` is rebuilt as the auth cert fingerprint\r\n(if present) plus the new CSR cert fingerprint. The previous session cert fingerprint is replaced.\r\n\r\n### Certificate Binding Verification\r\n\r\nWhen a token carries `z_cfs`, the controller enforces certificate binding on the refresh token\r\nand token exchange endpoints:\r\n\r\n- If `z_cfs` is non-empty, the TLS leaf certificate must match at least one fingerprint in\r\n  `z_cfs`. Requests without a matching certificate are rejected.\r\n- If `z_cfs` is empty, the controller falls back to SPIFFE ID verification, checking whether\r\n  the leaf certificate's SAN URI references the correct API session.\r\n\r\nA session that starts without `z_cfs` can transition to having it by submitting a CSR during\r\na refresh. Once `z_cfs` is present, it cannot be removed.\r\n\r\n### Controller Capability\r\n\r\nControllers advertise `OIDC_AUTH_WITH_CSR` in the `/version` capabilities list when OIDC is\r\nenabled. SDKs can check for this capability before attempting CSR submission.\r\n\r\n### Requirements\r\n\r\n- The CSR must be a valid PEM-encoded PKCS#10 certificate request. Invalid CSRs are rejected\r\n  with a 400 Bad Request error in OIDC error format.\r\n- The controller only uses the public key from the CSR. Subject, DNS names, IP addresses,\r\n  email addresses, and URI SANs in the CSR are ignored.\r\n- Issued certificates have a one-year lifetime.\r\n- Only the leaf certificate fingerprint is tracked in token claims. Intermediate certificates\r\n  in the TLS chain are not considered.\r\n\r\n## Connect Events Pool\r\n\r\nThe controller now uses per-router, single-worker goroutine pools to process identity\r\nconnect/disconnect events. Previously each router connection spawned a dedicated\r\ngoroutine that was never cleaned up on disconnect, leaking a goroutine per reconnect\r\ncycle. Under churn (e.g., chaos testing with hundreds of routers) this could accumulate\r\ntens of thousands of leaked goroutines and destabilize the controller.\r\n\r\nUsing a single-worker pool per router also ensures that events from the same router are\r\nalways processed in FIFO order. Previously, a shared multi-worker pool could process a\r\nfull-state sync after a newer incremental event from the same router, causing identities\r\nto be incorrectly marked as disconnected.\r\n\r\nThe pool is configurable in the controller config file:\r\n\r\n```yaml\r\nconnectEvents:\r\n  queueSize: 5    # per-router work queue depth (default: 5)\r\n  idleTime:  30s  # worker idle timeout before exit (default: 30s)\r\n```\r\n\r\nThe defaults are suitable for most deployments. Each router's worker starts on demand\r\nand exits after the idle timeout, so no goroutines are held when there is no work.\r\n\r\nNote: the `minWorkers` and `maxWorkers` settings have been removed. Each router's pool\r\nis fixed at one worker for correctness.\r\n\r\n## Community Contributors\r\n\r\nThank you to the following community members for their contributions:\r\n\r\n* @ffaraone - Azure Service Bus event sink\r\n* @dmuensterer - OIDC token refresh fixes\r\n* @nenkoru - Controller isleader health check endpoint\r\n* Jan Starkl - UPDB auth attempts fix\r\n* Mamy Ratsimbazafy - uint16 port range fix\r\n\r\n## Basic Permission System (BETA)\r\n\r\nAdded a basic permission system that allows more control over identity access to controller management API operations. \r\nThis replaces the previous binary admin/non-admin model with a more flexible permission system.\r\n\r\n**NOTE:** This feature is in BETA, primarily so we can get feedback on which permissions make sense. The implementation is unlikely to change\r\nbut the set of exposed permissions may grow, shrink or change based on user feedback. \r\n\r\n### Permission Model\r\n\r\nThe permission system supports three levels of authorization:\r\n\r\n  1. **Global Permissions**: System-wide access levels\r\n     - `admin` - Full access to all operations. This is still controlled by the `isAdmin` flag on identity\r\n     - `admin_readonly` - Read-only access to all resources except debugging facilities inspect and validate\r\n\r\n  2. **Entity-Level Permissions**: Full CRUD access to specific entity types\r\n     - Granting an entity-level permission (e.g., `service`) provides complete create, read, update, and delete access for that entity type\r\n\r\n  3. **Action-Level Permissions**: Specific operation access on entity types\r\n     - Fine-grained control using the pattern `<entity>.<action>` (e.g., `service.read`, `identity.update`)\r\n     - Supports `create`, `read`, `update`, and `delete` actions per entity type\r\n\r\n### Supported Entity Permissions\r\n\r\nThe following entity-level permissions are available:\r\n\r\n- `auth-policy` - Authentication policy management\r\n- `ca` - Certificate Authority management\r\n- `config` - Configuration management\r\n- `config-type` - Configuration type management\r\n- `edge-router-policy` - Edge router policy management\r\n- `enrollment` - Enrollment management\r\n- `external-jwt-signer` - External JWT signer management\r\n- `identity` - Identity management\r\n- `posture-check` - Posture check management\r\n- `router` - Edge and transit router management\r\n- `service` - Service management\r\n- `service-policy` - Service policy management\r\n- `service-edge-router-policy` - Service edge router policy management\r\n- `terminator` - Terminator management\r\n- `ops` - Operational resources (API sessions, sessions, circuits, links, inspect and validate)\r\n\r\n### Permission Assignment\r\n\r\nPermissions are assigned to identities via the `permissions` field in the identity resource. Multiple permissions can be granted to a single identity, and permissions are additive.\r\n\r\n### Cross-Entity Operations\r\n\r\nListing related entities through an entity's endpoints requires appropriate permissions for the related entity type. For example:\r\n- Listing services for a service-policy requires `service.read` permission\r\n- Listing identities for an edge-router-policy requires `identity.read` permission\r\n- Listing configs for a service requires `config.read` permission\r\n\r\n**NOTE:** \r\nMore permissions than expected may be required when performing actions through the CLI or ZAC. Take for example, when an identity \r\nhas `config.create` and is attempting to create a new config. The CLI may fail if the identity doesn't have `config-type.read`\r\nas well because it will need to look up the config type id that corresponds to the given config type name.\r\n\r\nSimilar cross entity read permissions may be required when creating services.\r\n\r\n### Admin Protection\r\n\r\nNon-admin identities cannot:\r\n- Create identities with the `isAdmin` flag\r\n- Create identities with any permissions granted\r\n- Modify admin-related fields on existing identities\r\n- Update or delete admin identities\r\n- Grant permissions to identities\r\n\r\nThese protections ensure that privilege escalation is prevented and admin access remains controlled.\r\n\r\n\r\n## Binding Controller APIs With Identity\r\n\r\nController APIs can now be bound to an OpenZiti overlay network identity, allowing secure communication through\r\nthe Ziti network. This is useful for scenarios where you want to expose controller APIs only through the overlay\r\nnetwork rather than on a standard network interface.\r\n\r\n### Configuration Structure\r\n\r\nA standard `bindPoint` configuration looks like this:\r\n```text\r\n    bindPoints:\r\n      - interface: 127.0.0.1:18441\r\n        address: 127.0.0.1:18441\r\n```\r\n\r\nTo bind controller APIs to an OpenZiti identity, add an additional `identity` block to your `bindPoints`. The\r\nidentity configuration specifies where to load the Ziti identity file and which service to bind it to:\r\n\r\n```text\r\n    bindPoints:\r\n      - interface: 127.0.0.1:18441\r\n        address: 127.0.0.1:18441\r\n      - identity:\r\n          file: \"c:/temp/ctrl.testing/ctrl.identity.json\"\r\n          service: \"mgmt\"\r\n```\r\n\r\n### Supported Configuration Options\r\n\r\n- `file`: Path to a Ziti identity JSON file containing the controller's identity and enrollment certificate\r\n- `env`: Name of an environment variable containing a base64-encoded Ziti identity (alternative to `file`)\r\n- `service`: The name of the Ziti service to bind the controller API to\r\n\r\n### Using Environment Variables\r\n\r\nFor deployments where storing identity files on disk is not preferred, you can reference a base64-encoded\r\nidentity file from an environment variable. The environment variable should contain the base64-encoded contents\r\nof the identity JSON file.\r\n\r\nFor example, if an environment variable named `ZITI_CTRL_IDENTITY` contains a base64-encoded identity file:\r\n\r\n```text\r\n    bindPoints:\r\n      - interface: 127.0.0.1:18441\r\n        address: 127.0.0.1:18441\r\n      - identity:\r\n          env: ZITI_CTRL_IDENTITY\r\n          service: \"mgmt\"\r\n```\r\n\r\n### IPv6 Support\r\n\r\nBoth IPv4 and IPv6 addresses are supported for standard bind points. IPv6 addresses should be specified in bracket\r\nnotation with a port number:\r\n\r\n```text\r\n    bindPoints:\r\n      - interface: \"[::1]:18441\"\r\n        address: \"[::1]:18441\"\r\n      - identity:\r\n          file: \"/path/to/identity.json\"\r\n          service: \"mgmt\"\r\n```\r\n\r\n## CLI Enhancements for Identity-Based Connections\r\n\r\nThe `ziti edge login` command and REST client utilities have been enhanced to support identity-based connections\r\nthrough the Ziti overlay network.\r\n\r\n### New `--network-identity` Flag for `ziti edge login`\r\n\r\nThe `ziti edge login` command now includes a `--network-identity` flag that allows you to authenticate to a Ziti\r\ncontroller through the overlay network using a Ziti identity:\r\n\r\n```bash\r\nziti edge login https://ziti.mgmt.apis.local:1280 \\\r\n  --username myuser \\\r\n  --password mypass \\\r\n  --network-identity /path/to/identity.json\r\n```\r\n\r\nThis is useful when the controller is only accessible through the Ziti overlay network or when you want to ensure\r\nall communication to the controller flows through the overlay for security purposes.\r\n\r\n### Identity Resolution Order\r\n\r\nWhen establishing connections, identities are resolved in the following order:\r\n\r\n1. **Command-line flag**: The `--network-identity` flag takes precedence\r\n2. **Environment variable**: If `ZITI_CLI_NETWORK_ID` is set and contains a base64-encoded identity, it is used\r\n3. **Cached identity file**: If a network identity was saved from a previous login in the Ziti config directory, it may be used\r\n\r\nThis layered approach allows for flexibility in deployment scenarios:\r\n- Development: Use command-line flags for quick testing\r\n- Automation: Use environment variables in CI/CD pipelines\r\n- Production: Cache identities securely for repeated access\r\n\r\n#### Dialing Modes When Authenticating\r\n\r\nThe CLI supports two dialing modes:\r\n\r\n**Intercept-based Dialing (Default)**\r\nBy default, URLs are expected to leverage intercepts. Create a service with an appropriate intercept config and use\r\nthe intercept address when dialing. This is the standard mode for most use cases. For example, given a service with\r\nthe intercept `ziti.mgmt.apis.local`\r\n```bash\r\nziti edge login https://ziti.mgmt.apis.local:1280 \\\r\n  --username myuser \\\r\n  --password mypass \\\r\n  --network-identity /path/to/identity.json\r\n```\r\n\r\n**Identity-aware Dialing (Addressable Terminators)**\r\nTo support addressable terminators-based dialing, specify a user in the URL. This activates dial-by-identity\r\nfunctionality. The URL format should be `identity-to-dial@service-name-to-dial`. For example:\r\n```bash\r\nziti edge login https://my-identity@my-service:1280 \\\r\n  --username myuser \\\r\n  --password mypass \\\r\n  --network-identity /path/to/identity.json\r\n```\r\n\r\nIn this mode, the transport extracts the identity from the URL and uses it to establish a direct connection to\r\nthe specified service via the addressable terminator.\r\n\r\n\r\n## OIDC/JWT Token-based Enrollment\r\n\r\nOpenZiti now supports provisioning identities just-in-time through OIDC/JWT token enrollment. External identity \r\nproviders can be configured to allow identities to enroll using JWT tokens, with support for the resulting \r\nidentities to use certificate or token authentication.\r\n\r\n### External JWT Signer Configuration\r\n\r\nExternal JWT signers are configured via the Edge Management API to define enrollment behavior with the following new \r\nenrollment-specific properties:\r\n\r\n- **enrollToCertEnabled** - When enabled, identities can exchange a JWT token and a certificate signing request (CSR) \r\n        for a client certificate during enrollment. The certificate can then be used for standard certificate-based\r\n        authentication.\r\n\r\n- **enrollToTokenEnabled** - When enabled, identities can use a JWT token to enroll. The current token or future tokens\r\n        may be used for authentication.\r\n\r\n- **enrollNameClaimsSelector** - Specifies which JWT claim contains the identity name. Accepts a JSON pointer \r\n        (e.g., `/preferred_username`) or a simple property name (e.g., `preferred_username`, automatically converted to\r\n        `/preferred_username`). Defaults to `/sub` if not specified. The extracted value becomes the identity name in Ziti.\r\n\r\n- **enrollAttributeClaimsSelector** - Specifies which JWT claims to extract as identity attributes during enrollment.\r\n        Accepts a JSON pointer (e.g., `/roles`) or a simple property name (e.g., `roles`). Extracted attributes are \r\n        applied to the newly enrolled identity for use in authorization policies.\r\n\r\n- **enrollAuthPolicyId** - Specifies the authentication policy to apply to newly enrolled identities. This determines\r\n        what authentication methods are available for the identity post-enrollment.\r\n\r\nAdditionally the existing property named **claimsProperty** that specifies external id to match identities to:\r\n\r\n- now supports a JSON pointer (e.g., `/id`) or a simple property name (e.g., `id`)\r\n- is used to populate the `externalId` field of the identity\r\n\r\n### Enrollment Paths\r\n\r\n#### Certificate Enrollment (enrollToCertEnabled)\r\n\r\nWhen certificate enrollment is enabled, unauthenticated users can:\r\n\r\n1. Obtain a list of available IdPs from the public Edge Client API `GET /external-jwt-signers` endpoint, where \r\n   `enrollToCertEnabled` is set to `true`\r\n2. Obtain a JWT from the configured OIDC provider\r\n3. Generate a certificate signing request (CSR)\r\n4. Submit an enrollment request with the JWT and CSR\r\n5. Have their identity created in Ziti with attributes extracted from JWT claims\r\n6. Receive a signed client certificate for certificate-based authentication\r\n\r\n#### Token Enrollment (enrollToTokenEnabled)\r\n\r\nWhen token enrollment is enabled, unauthenticated users can:\r\n\r\n1. Obtain a list of available IdPs from the public Edge Client API `GET /external-jwt-signers` endpoint, where \r\n   `enrollToTokenEnabled` is set to `true`\r\n1. Obtain a JWT from the configured OIDC provider\r\n2. Submit an enrollment request with the JWT\r\n3. Have their identity created in Ziti with attributes extracted from JWT claims\r\n4. Receive a Ziti API token for token-based authentication\r\n\r\n### Edge Management API\r\n\r\nThe Edge Management API provides full CRUD operations for configuring external JWT signers:\r\n\r\n- `POST /external-jwt-signers` - Create a new external JWT signer with all configuration options\r\n- `GET /external-jwt-signers` - List all configured external JWT signers\r\n- `GET /external-jwt-signers/{id}` - Retrieve a specific signer configuration\r\n- `PUT /external-jwt-signers/{id}` - Update all fields of a signer\r\n- `PATCH /external-jwt-signers/{id}` - Partially update a signer\r\n- `DELETE /external-jwt-signers/{id}` - Delete a signer\r\n\r\n### Edge Client API\r\n\r\nThe Edge Client API exposes a reduced set of external JWT signer information for unauthenticated enrollment requests:\r\n\r\n- `GET /external-jwt-signers` - List available JWT signers with enrollment capabilities\r\n\r\nThe client API response includes the following fields for each signer:\r\n\r\n- `name` - Signer name\r\n- `externalAuthUrl` - URL where users obtain JWT tokens\r\n- `clientId` - OIDC client ID\r\n- `scopes` - Requested OIDC scopes\r\n- `openIdConfigurationUrl` - OIDC discovery endpoint\r\n- `audience` - Expected token audience\r\n- `targetToken` - Token type to use (ACCESS or ID)\r\n- **`enrollToCertEnabled`** - Flag indicating certificate enrollment is available\r\n- **`enrollToTokenEnabled`** - Flag indicating token enrollment is available\r\n\r\n### CLI Commands\r\n\r\n**Create an external JWT signer with enrollment options:**\r\n```\r\nziti edge controller create ext-jwt-signer <name> <issuer> \\\r\n  --jwks-endpoint <url> \\\r\n  --audience <audience> \\\r\n  --enroll-to-cert \\\r\n  --enroll-to-token=false \\\r\n  --enroll-name-claims-selector preferred_username \\\r\n  --enroll-attr-claims-selector roles \\\r\n  --enroll-auth-policy <policy-id-or-name>\r\n```\r\n\r\n**Update enrollment options on an existing signer:**\r\n```\r\nziti edge controller update ext-jwt-signer <name|id> \\\r\n  --enroll-to-cert \\\r\n  --enroll-auth-policy <policy-id-or-name>\r\n```\r\n\r\n**List external JWT signers:**\r\n```\r\nziti edge controller list ext-jwt-signers\r\n```\r\n\r\n## Clustering Performance Improvements\r\n\r\nIn previous releases, model updates were submitted to raft one at at time. This prevented \r\nraft from being efficient by allowing command batching. This release allows multiple \r\nmodel updates to be in-flight at the same time. \r\n\r\nNew Configuration Options\r\n\r\n1. Raft Apply Timeout (cluster.applyTimeout)\r\n\r\nLocation: Controller configuration file, under the cluster section\r\nType: Duration\r\nDefault: 5s\r\nDescription: Timeout for applying commands to the Raft distributed log. Commands that exceed this timeout will trigger adaptive rate limiter backoff.\r\n\r\nExample:\r\n```\r\ncluster:\r\n  applyTimeout: 10s\r\n```\r\n\r\n2. Cluster Rate Limiter Configuration (cluster.rateLimiter)\r\n\r\nA new adaptive rate limiter that controls the submission of commands to the Raft cluster. Unlike the existing command rate limiter, this specifically manages in-flight Raft operations with adaptive window sizing.\r\n\r\nConfiguration Structure:\r\n```\r\ncluster:\r\n  rateLimiter:\r\n    enabled: true\r\n    minSize: 5\r\n    maxSize: 250\r\n    timeout: 30s\r\n```\r\n\r\nSub-options:\r\n\r\n  - enabled (boolean)\r\n    - Default: true\r\n    - Description: Enable/disable adaptive rate limiting for Raft command submission\r\n  - minSize (integer)\r\n    - Default: 5\r\n    - Minimum: 1\r\n    - Description: Minimum window size for concurrent in-flight Raft operations\r\n  - maxSize (integer)\r\n    - Default: 250\r\n    - Description: Maximum window size for concurrent in-flight Raft operations. Must be >= minSize\r\n  - timeout (duration)\r\n    - Default: 30s\r\n    - Description: Time after which outstanding work is assumed to have failed if not marked completed\r\n\r\n3. Restart Self on Snapshot (cluster.restartSelfOnSnapshot)\r\n\r\nLocation: Controller configuration file, under cluster section\r\nType: Boolean\r\nDefault: false\r\nDescription: When true, the controller will automatically restart itself when restoring a snapshot to an initialized system. When false, the controller will exit with code 0, requiring external process management to restart it.\r\n\r\nExample:\r\n```\r\ncluster:\r\n    restartSelfOnSnapshot: true\r\n```\r\n\r\n### New Metrics\r\n\r\nThe adaptive rate limiter exposes three new metrics:\r\n\r\n  1. raft.rate_limiter.queue_size (gauge)\r\n    - Current number of operations queued/in-flight\r\n  2. raft.rate_limiter.work_timer (timer)\r\n    - Duration of rate-limited operations\r\n  3. raft.rate_limiter.window_size (gauge)\r\n    - Current adaptive window size\r\n\r\n## Rate Limiter Algorithm Improvements\r\n\r\nThe adaptive rate limiter tracker now uses a success rate metric to decide when to grow or shrink its\r\nconcurrency window, instead of using raw queue position. An exponentially decaying histogram tracks the\r\nratio of successes to backoffs. When the success rate exceeds a configurable threshold, the window is\r\ngrown by a multiplicative increase factor. When it falls below the threshold, the window is shrunk by a\r\nmultiplicative decrease factor. The check intervals for increase and decrease are independently configurable.\r\n\r\nThis approach produces smoother, more stable window adjustments under varying load, avoiding the\r\nover-aggressive shrinking that could occur when queue position alone was used as the signal.\r\n\r\nThis specific rate limiter implementation is used in three places:\r\n* **Controller TLS handshake rate limiting** - controls the rate of incoming TLS handshakes on the controller\r\n* **Raft command submission** - controls the rate of commands submitted to the Raft distributed log\r\n* **Router control channel rate limiting** - controls the rate of requests from the router to the controller\r\n\r\nNote: this work was done in advance of enabling the TLS handshake rate limiter by default. The TLS\r\nhandshake rate limiter is not yet enabled by default, but can be enabled via the `tls.rateLimiter`\r\nconfiguration section.\r\n\r\nNew configuration options (available under each `rateLimiter` section):\r\n\r\n  - successThreshold (float)\r\n    - Default: 0.9\r\n    - Description: Success rate threshold above which the window size will be increased and below which it will be decreased\r\n  - increaseFactor (float)\r\n    - Default: 1.02\r\n    - Description: Multiplier applied to the current window size when growing. Must be greater than 1\r\n  - decreaseFactor (float)\r\n    - Default: 0.9\r\n    - Description: Multiplier applied to the current window size when shrinking. Must be between 0 and 1\r\n  - increaseCheckInterval (integer)\r\n    - Default: 10\r\n    - Description: Number of successes between window size increase checks\r\n  - decreaseCheckInterval (integer)\r\n    - Default: 10\r\n    - Description: Number of backoffs between window size decrease checks\r\n\r\n## Background Processing for Identity Updates\r\n\r\nIdentity environment and authenticator updates that occur during authentication are now processed asynchronously in the background. \r\nThis prevents authentication requests from blocking when the system is under load, significantly improving resilience during thundering herd scenarios.\r\n\r\nWhen the background queue fills up, updates can be dropped as they will be refreshed on the next authentication attempt. \r\nThis allows the system to gracefully handle load spikes without impacting authentication performance.\r\n\r\nFor now, dropping entries when the queue fills will be disabled by default, but can be enabled, see below.\r\n\r\n### Configuration\r\n\r\nA new `command.background` configuration section controls the background processing behavior:\r\n\r\n```yaml\r\n  command:\r\n    background:\r\n      enabled: true           # Enable background processing (default: true)\r\n      queueSize: 1000        # Maximum queue size (default: 1000)\r\n      dropWhenFull: true     # Drop updates when queue is full (default: false)\r\n      delayThreshold: 50ms   # The threshold for how long updates are taking before starting to background updates\r\n```\r\n\r\nNote that the `commandRateLimiter` configuration section may instead be specified under `command` as `rateLimiter`.\r\n\r\nExample:\r\n\r\n```yaml\r\n  command:\r\n    background:\r\n      enabled: true\r\n      queueSize: 250\r\n      dropWhenFull: false\r\n      delayThreshold: 50ms\r\n    rateLimiter:\r\n      enabled:   true\r\n      maxQueued: 25\r\n```\r\n\r\nNote that if command rate limiter configuration is specified in both locations, the settings under `command` will take \r\nprecedence. The standalone `commandRateLimiter` section may be deprecated in the future.\r\n\r\n### Metrics\r\n\r\nWhen background processing is enabled, the following metrics are exposed:\r\n\r\n- command.background.queue_size - Current number of queued background tasks\r\n- command.background.worker_count - Current number of worker goroutines\r\n- command.background.busy_workers - Number of workers currently processing tasks\r\n- command.background.work_timer - Timer tracking background task execution (includes histogram, meter, and count)\r\n- command.background.dropped_entries - Count of dropped updates when queue is full (only when dropWhenFull is enabled)\r\n\r\n## New proxy.v1 Config Type\r\n\r\n*Originally released in 1.7.0*\r\n\r\nAdded support for dynamic service proxies with configurable binding and protocol options.\r\nThis allows Edge Routers and Tunnelers to create proxy endpoints that can forward traffic for Ziti services.\r\n\r\nThis differs from intercept.v1 in that intercept.v1 will intercept traffic on specified\r\nIP addresses or DNS entries to forward to a service using tproxy or tun interface,\r\ndepending on implementation.\r\n\r\nA proxy on the other hand will just start a regular TCP/UDP listener on the configured port,\r\nso traffic will have to be configured for that destination.\r\n\r\nExample proxy.v1 Configuration:\r\n\r\n```\r\n  {\r\n    \"port\": 8080,\r\n    \"protocols\": [\"tcp\"],\r\n    \"binding\": \"0.0.0.0\"\r\n  }\r\n```\r\n\r\nConfiguration Properties:\r\n  - port (required): Port number to listen on (1-65535)\r\n  - protocols (required): Array of supported protocols (tcp, udp)\r\n  - binding (optional): Interface to bind to. For the ER/T defaults to the configured lanIF config property.\r\n\r\nThis config type is currently supported by the ER/T when running in either proxy or tproxy mode.\r\n\r\n## Alert Events (BETA)\r\n\r\n*Originally released in 1.7.0*\r\n\r\nA new alert event type has been added to allow Ziti components to emit alerts for issues that network operators can address.\r\nAlert events are generated when components encounter problems such as service configuration errors or resource\r\navailability issues.\r\n\r\nAlert events include:\r\n  - Alert source type and ID (currently supports routers, with controller and SDK support planned for future releases)\r\n  - Severity level (currently supports error, with info and warning planned for future releases)\r\n  - Alert message and supporting details\r\n  - Related entities (router, identity, service, etc.) associated with the alert\r\n\r\nExample alert event when a router cannot bind a configured network interface:\r\n\r\n```\r\n  {\r\n    \"namespace\": \"alert\",\r\n    \"event_src_id\": \"ctrl1\",\r\n    \"timestamp\": \"2021-11-08T14:45:45.785561479-05:00\",\r\n    \"alert_source_type\": \"router\",\r\n    \"alert_source_id\": \"DJFljCCoLs\",\r\n    \"severity\": \"error\",\r\n    \"message\": \"error starting proxy listener for service 'test'\",\r\n    \"details\": [\r\n      \"unable to bind eth0, no address\"\r\n    ],\r\n    \"related_entities\": {\r\n      \"router\": \"DJFljCCoLs\",\r\n      \"identity\": \"DJFljCCoLs\",\r\n      \"service\": \"3DPjxybDvXlo878CB0X2Zs\"\r\n    }\r\n  }\r\n```\r\n\r\nAlert events can be consumed through the standard event system and logged to configured event handlers for monitoring and alerting purposes.\r\n\r\nThese events are currently in Beta, as the format is still subject to change. Once they've been in use in production for a while\r\nand proven useful, they will be marked as stable.\r\n\r\n## Azure Service Bus Event Sink\r\n\r\n*Originally released in 1.7.0. Contributed by @ffaraone.*\r\n\r\nAdds support for streaming controller events to Azure Service Bus.\r\nThe new logger enables real-time event streaming from the OpenZiti controller to Azure Service Bus\r\nqueues or topics, providing integration with Azure-based monitoring and analytics systems.\r\n\r\nTo enable the Azure Service Bus event logger, add configuration to the controller config file under the events section:\r\n\r\n```\r\n  events:\r\n    serviceBusLogger:\r\n      subscriptions:\r\n        - type: circuit\r\n        - type: session\r\n        - type: metrics\r\n          sourceFilter: .*\r\n          metricFilter: .*\r\n        # Add other event types as needed\r\n      handler:\r\n        type: servicebus\r\n        format: json\r\n        connectionString: \"Endpoint=sb://your-namespace.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=your-key\"\r\n        topic: \"ziti-events\"          # Use 'topic' for Service Bus topic\r\n        # queue: \"ziti-events-queue\"  # Or use 'queue' for Service Bus queue\r\n        bufferSize: 100                # Optional, defaults to 50\r\n```\r\n\r\n- Required configuration:\r\n    - format: Event format, currently supports only json\r\n    - connectionString: Azure Service Bus connection string\r\n    - Either topic or queue: Destination name (mutually exclusive)\r\n\r\n- Optional configuration:\r\n    - bufferSize: Internal message buffer size (default: 50)\r\n\r\n## OIDC is now enabled by default\r\n\r\nThe controller now automatically adds the `edge-oidc` API binding to any web listener that hosts\r\nthe `edge-client` API, even when `edge-oidc` is not explicitly listed in the `web` section of the\r\nconfiguration. This means OIDC-based authentication is available out of the box without requiring\r\nchanges to existing controller configurations.\r\n\r\n### Where OIDC binds\r\n\r\nThe `edge-oidc` binding is added to the same web listener(s) as `edge-client`. If `edge-client` is\r\nhosted on `127.0.0.1:1280`, the OIDC endpoints will be available on that same address under the\r\n`/oidc` path (e.g. `https://127.0.0.1:1280/oidc/.well-known/openid-configuration`).\r\n\r\nThe controller capabilities returned by `GET /version` will include `OIDC_AUTH` and the\r\n`edge-oidc` entry will be present in `apiVersions` when OIDC is active.\r\n\r\n### Opting out\r\n\r\nIf OIDC should not be enabled automatically, set `disableOidcAutoBinding: true` under `edge.api`:\r\n\r\n```yaml\r\nedge:\r\n  api:\r\n    address: 127.0.0.1:1280\r\n    sessionTimeout: 30m\r\n    disableOidcAutoBinding: true\r\n```\r\n\r\nWhen `disableOidcAutoBinding` is `true`, OIDC will only be active if `edge-oidc` is explicitly\r\nlisted as a binding in the `web` section. \r\n\r\nWhen OIDC is not enabled, all (SDK) clients will revert to using legacy authentication.\r\nLegacy authentication does not support multiple controllers or HA in general. Clients will treat\r\ntheir controller as if it is a single controller instance and will function as if no other controllers\r\nexist.\r\n\r\n\r\n## WWW-Authenticate Headers\r\n\r\nThe controller's Edge APIs now include `WWW-Authenticate` headers on `401 Unauthorized` responses,\r\nper issuer: https://github.com/openziti/ziti/issues/3356. These headers provide insight into why\r\na token was rejected. The main benefit of these headers is to convey information for JWT backed\r\nAPI Sessions and API Session authentication where a token may not have been provided (missing),\r\nis used beyond its expiration date (expired), or the token has become invalid for any other\r\nreason (invalid).\r\n\r\n`www-authenticate` headers are provided as a single header instance with multiple challenge values\r\nseparate by commas.\r\n\r\n### No Credentials Provided\r\n\r\nWhen a request hits a protected endpoint without any credentials, a single `WWW-Authenticate` header\r\nis returned listing both accepted auth schemes as comma-separated challenges:\r\n\r\n```\r\nWWW-Authenticate: zt-session realm=\"zt-session\" error=\"missing\" error_description=\"no matching token was provided\",Bearer realm=\"openziti-oidc\" error=\"missing\" error_description=\"no matching token was provided\"\r\n```\r\n\r\n### Token Errors\r\n\r\nWhen a token is present but cannot be accepted, the header identifies the scheme and what went wrong:\r\n\r\n```\r\nWWW-Authenticate: Bearer realm=\"openziti-oidc\" error=\"expired\" error_description=\"token expired\"\r\nWWW-Authenticate: Bearer realm=\"openziti-oidc\" error=\"invalid\" error_description=\"token is invalid\"\r\nWWW-Authenticate: zt-session realm=\"zt-session\" error=\"invalid\" error_description=\"token is invalid\"\r\n```\r\n\r\n### OIDC External JWT — Primary Authentication\r\n\r\nWhen an auth policy requires an external JWT signer for primary authentication (e.g., a PKCE flow\r\nbacked by an ext-jwt signer), the header identifies which signers are accepted and what went wrong.\r\nMultiple accepted signers are pipe-delimited in the `id` and `issuer` parameters:\r\n\r\n```\r\nWWW-Authenticate: Bearer realm=\"openziti-primary-ext-jwt\" error=\"missing\" error_description=\"no matching token was provided\" id=\"signer-id-1|signer-id-2\" issuer=\"https://issuer1.example.com|https://issuer2.example.com\"\r\n```\r\n\r\nThe `error` value follows the same `missing`/`expired`/`invalid` pattern as standard bearer token errors.\r\n\r\n### OIDC External JWT — Secondary / MFA Authentication\r\n\r\nWhen an auth policy requires an external JWT signer as a secondary factor (step-up after primary\r\nauth succeeds), the header identifies the single required signer. The `error` value follows the\r\nsame `missing`/`expired`/`invalid` pattern:\r\n\r\n```\r\nWWW-Authenticate: Bearer realm=\"openziti-secondary-ext-jwt\" error=\"missing\" error_description=\"no matching token was provided\" id=\"<signer-id>\" issuer=\"<issuer>\"\r\n```\r\n\r\n### Anonymous Endpoints\r\n\r\nUnauthenticated endpoints such as version information do not return `WWW-Authenticate` headers.\r\n\r\n## HA Preferred Leaders\r\n\r\nControllers can be marked as a preferred leader. \r\n\r\n**Example Config**\r\n```yaml\r\ncluster:\r\n  dataDir: /home/{{ .Model.MustVariable \"credentials.ssh.username\" }}/fablab/ctrldata\r\n  preferredLeader: true\r\n```\r\n\r\nIf a controller that is not marked preferredLeader becomes a preferredLeader, it will check \r\nif there's a node available that is marked as preferred. If there is one, or one later\r\njoins the cluster, the non-preferred node will attempt to transfer leadership to the \r\nnode that is marked as preferred.\r\n\r\n## Expanded Dynamic Cost Range\r\n\r\nThe dynamic cost range for smart routing has been expanded beyond the previous 64K limit. Under high\r\nload, terminators could saturate the cost space, making dynamic cost values meaningless for routing\r\ndecisions and leading to uneven load distribution. The expanded range allows for more granular cost\r\ndifferentiation even under heavy load.\r\n\r\n## Circuit ID and Error in Dial Failures\r\n\r\nDial failures now return the circuit ID and, when available, the error that caused the circuit to fail.\r\nPreviously, the circuit ID was only returned on successful dials. Note that SDKs will need to be\r\nupdated to surface the circuit id when a dial failure happens.\r\n\r\n## Multi-Underlay Control Channels\r\n\r\nRouter-to-controller control channels now support multiple underlays with priority-based message routing.\r\nThis allows time-sensitive control messages (heartbeats, routing, circuit requests) to be separated from\r\noperational data (metrics, inspections) across dedicated TCP connections, preventing bulk operations from\r\ndelaying user-affecting control plane traffic. This feature does not yet allow specifying multiple \r\nnetwork interfaces to use, to load balance data across.\r\n\r\n## Dialing Identity Forwarded to Hosting SDK\r\n\r\nThe identity ID and name of the dialing client are now forwarded to the hosting SDK when a circuit is\r\nestablished. This allows hosting applications to identify which identity initiated the connection,\r\nenabling identity-aware request handling on the server side. This will require SDK updates to add this\r\nto the API for hosting applications.\r\n\r\n## Controller-Initiated Control Channel Dials (BETA)\r\n\r\nControllers can now dial routers to establish control channels. Previously, routers were solely\r\nresponsible for dialing controllers. This feature is designed for deployments where one or more\r\ncontrollers are in a private network that routers cannot reach, but the controllers can dial out.\r\nA common scenario is an HA cluster where some controllers are publicly reachable and some are in\r\na private network. External routers connect to the public controllers normally, and the private\r\ncontrollers dial out to the routers.\r\n\r\n### Router Configuration\r\n\r\nRouters can configure one or more control channel listeners. Each listener specifies a bind address,\r\nan advertise address (reported to the controller), and optional groups for matching.\r\n\r\n```yaml\r\nctrl:\r\n  listeners:\r\n    - bind: tls://0.0.0.0:6262\r\n      advertise: tls://router.example.com:6262\r\n      groups:\r\n        - default\r\n```\r\n\r\nThe router is the authoritative source of ctrl channel listener information, similar to link\r\nlisteners. When a router connects to any controller, it reports its configured `ctrlChanListeners`\r\nand the controller data model is updated automatically. This means that in most deployments —\r\nwhere the router can reach at least one controller — no manual configuration of listener addresses\r\nis needed on the controller side.\r\n\r\nThe `ctrlChanListeners` field can also be set via the CLI for cases where a router cannot reach\r\nany controller and thus cannot initialize the data model itself:\r\n\r\n```bash\r\nziti edge update edge-router myRouter --bootstrap-ctrl-chan-listener 'tls://router.example.com:6262=group1,group2'\r\n```\r\n\r\nGroups default to `[\"default\"]` if not specified.\r\n\r\n### Controller Configuration\r\n\r\nThe controller dialer is disabled by default and must be explicitly enabled. When enabled, the\r\ncontroller will dial routers that have control channel listeners configured and are not already\r\nconnected.\r\n\r\n```yaml\r\nctrl:\r\n  dialer:\r\n    enabled: true\r\n    groups:\r\n      - default\r\n    dialDelay: 30s\r\n    minRetryInterval: 1s\r\n    maxRetryInterval: 5m\r\n    retryBackoffFactor: 1.5\r\n    fastFailureWindow: 5s\r\n    queueSize: 32\r\n    maxWorkers: 10\r\n```\r\n\r\n- `enabled` - Enables the controller dialer (default: `false`)\r\n- `groups` - List of groups to match against router listener groups (default: `[\"default\"]`)\r\n- `dialDelay` - Delay before the controller starts dialing after boot (default: `30s`)\r\n- `minRetryInterval` - Minimum backoff delay between dial retries (default: `1s`)\r\n- `maxRetryInterval` - Maximum backoff delay between dial retries (default: `5m`)\r\n- `retryBackoffFactor` - Multiplier applied to the retry delay on each failure, jittered +/- 0.5 (default: `1.5`)\r\n- `fastFailureWindow` - If a connection drops within this window after connecting, backoff continues rather than resetting (default: `5s`)\r\n- `queueSize` - Maximum number of pending dial jobs in the worker pool queue (default: `32`)\r\n- `maxWorkers` - Maximum number of concurrent dial workers (default: `10`)\r\n\r\nThe controller will only dial routers whose listener groups overlap with the controller's configured\r\ngroups. When a router advertises multiple ctrl channel listener addresses, the dialer rotates through\r\nthem on each failure so that an unreachable address does not block attempts to the others.\r\n\r\n### Metrics\r\n\r\nThe controller dialer worker pool exposes the following metrics under the `ctrl_channel.dialer` prefix:\r\n\r\n- `ctrl_channel.dialer.queue_size` - Current number of pending dial jobs in the queue\r\n- `ctrl_channel.dialer.worker_count` - Current number of dial worker goroutines\r\n- `ctrl_channel.dialer.busy_workers` - Number of workers currently executing a dial\r\n- `ctrl_channel.dialer.work_timer` - Timer tracking the duration of each dial attempt\r\n\r\n## SDK Inspection Support\r\n\r\nNew CLI commands have been added for inspecting SDK state, useful for diagnosing terminator and\r\nconnectivity issues.\r\n\r\n**Note:** SDK inspection requires SDK support. Currently only the Go SDK supports inspection,\r\nas of version 1.5.0. Other SDKs will need to add support before these commands can be used\r\nwith them.\r\n\r\n### `ziti fabric inspect sdk`\r\n\r\nRetrieves SDK context inspection data from identities connected to routers.\r\n\r\n```\r\nziti fabric inspect sdk <target-selector> <identity-id>\r\n```\r\n\r\nThe `<target-selector>` is a regex matching router IDs (use `.*` for all routers). The\r\n`<identity-id>` is the identity whose SDK context you want to inspect. The command returns\r\ndetailed state from the connected SDK instance, including active services, terminators, and\r\nconnection status.\r\n\r\n### `ziti agent tunnel dump-sdk`\r\n\r\nDumps SDK context information from a running `ziti tunnel` process via the IPC agent.\r\n\r\n```\r\nziti agent tunnel dump-sdk\r\n```\r\n\r\nReturns JSON-formatted inspection data for all SDK contexts registered in the tunnel process,\r\nincluding service listeners, connections, and terminator state.\r\n\r\n## Revocation System Improvements\r\n\r\nWhen a session is refreshed, the old refresh token's revocation is no longer created\r\nsynchronously through raft. Instead, revocations are queued in memory and flushed in\r\nbatches on a configurable interval. This removes the database and raft as a bottleneck\r\non token refreshes. If the old token is close to expiring, the revocation is skipped\r\nentirely.\r\n\r\nNew configuration tunables under `edge.oidc`:\r\n\r\n| Key | Default | Description |\r\n|-----|---------|-------------|\r\n| `revocationMinTokenLifetime` | unset | Skip revocation if the old token expires within this duration (must be < 50% of `refreshTokenDuration`) |\r\n| `revocationBucketInterval` | `1m` | Bucket window for batching revocations before flushing through raft |\r\n| `revocationBucketMaxSize` | `200` | Max revocations per raft entry |\r\n| `revocationMaxQueued` | `25000` | Max revocations queued in memory before dropping |\r\n| `revocationEnforcerFrequency` | `1m` | How often expired revocations are purged (leader only) |\r\n\r\n## CLI Reorganization Details\r\n\r\nThe CLI has been reorganized so that edge and fabric entity management commands are \r\navailable directly at the top level. The `ziti edge` and `ziti fabric` command trees \r\nremain fully functional — the new top-level commands are additional entry points, not \r\nreplacements.\r\n\r\n### Top-Level CRUD Commands\r\n\r\nEntity create, delete, list, and update operations that previously required the \r\n`ziti edge` or `ziti fabric` prefix are now available directly:\r\n\r\n| New command | Previous command |\r\n|---|---|\r\n| `ziti create identity ...` | `ziti edge create identity ...` |\r\n| `ziti delete service ...` | `ziti edge delete service ...` |\r\n| `ziti list edge-routers` | `ziti edge list edge-routers` |\r\n| `ziti update identity ...` | `ziti edge update identity ...` |\r\n| `ziti list circuits` | `ziti fabric list circuits` |\r\n| `ziti delete terminator ...` | `ziti fabric delete terminator ...` |\r\n\r\nAll edge and fabric entities are available under the consolidated commands. When an \r\nentity name exists in both edge and fabric (e.g., `service`, `router`), the edge \r\nversion is the default. Fabric equivalents are accessible with a `fabric-` prefix \r\n(e.g., `ziti list fabric-services`).\r\n\r\n### Top-Level Login\r\n\r\nSession management is now available at the top level:\r\n\r\n| New command | Previous command |\r\n|---|---|\r\n| `ziti login` | `ziti edge login` |\r\n| `ziti login forget` | `ziti edge login forget` |\r\n| `ziti login use` | `ziti edge use` |\r\n\r\n### Breaking Change: `ziti create ca`\r\n\r\n`ziti create ca` now creates a Ziti edge Certificate Authority, matching the \r\nbehavior of `ziti edge create ca`. Previously, `ziti create ca` was a PKI command \r\nfor generating CA certificates on the local filesystem.\r\n\r\nThe PKI command is still available at its original location:\r\n\r\n```\r\nziti pki create ca ...\r\n```\r\n\r\nScripts that use `ziti create ca` for PKI operations should be updated to use \r\n`ziti pki create ca` instead.\r\n\r\n## Multiple DNS Upstreams\r\n\r\nThe tunneler's DNS resolver now accepts multiple upstream DNS servers and fans out recursive queries\r\nto all of them in parallel, rather than being limited to a single upstream. This is useful for split-horizon\r\nsetups where different resolvers are authoritative for different zones, and for environments where a primary\r\nresolver may be slow or unreachable.\r\n\r\n### CLI\r\n\r\nThe `ziti tunnel --dnsUpstream` flag is now a repeatable string slice. Upstreams can be listed by repeating\r\nthe flag or by passing a comma-separated value:\r\n\r\n```bash\r\nziti tunnel run \\\r\n  --dnsUpstream udp://10.96.0.10:53 \\\r\n  --dnsUpstream tcp://8.8.8.8:53\r\n```\r\n\r\n### Router Config\r\n\r\nIn `xgress_edge_tunnel` router configs, `options.dnsUpstream` now accepts either a single string (as before)\r\nor a list of strings. Existing configs continue to work unchanged:\r\n\r\n```yaml\r\n# single upstream (unchanged)\r\noptions:\r\n  dnsUpstream: udp://10.96.0.10:53\r\n\r\n# multiple upstreams\r\noptions:\r\n  dnsUpstream:\r\n    - udp://10.96.0.10:53\r\n    - tcp://8.8.8.8:53\r\n```\r\n\r\n### How Resolution Works\r\n\r\nWhen a query comes in, the resolver dispatches it to every configured upstream concurrently. The first\r\nresponse with `RCODE=NOERROR` wins and is returned to the client immediately, so split-horizon lookups\r\nwork even if one upstream is slow. If no upstream returns NOERROR, the resolver picks the best-ranked\r\nnon-NOERROR reply (NXDOMAIN > SERVFAIL > REFUSED) so authoritative negative answers aren't masked by\r\ntransport failures. If every upstream fails to respond, the query is treated as unanswerable and handled\r\nper the configured `dnsUnanswerable` disposition.\r\n\r\n## Agent Inspect\r\n\r\nThe `ziti agent` CLI now has a generic `inspect` subcommand that works against any ziti process\r\n(controller, router, or tunneler) over the local IPC agent channel. It sends the inspect request\r\ndirectly to the target process, so unlike `ziti fabric inspect` it doesn't fan out through the\r\ncontroller and doesn't require network connectivity to the target.\r\n\r\n```\r\nziti agent inspect <value> [values...]\r\n```\r\n\r\nValues are matched against whatever the target process exposes. Common inspect keys:\r\n\r\n* Routers: `stackdump`, `links`, `config`, `metrics`, `sdk-terminators`, `ert-terminators`,\r\n  `router-circuits`, `router-data-model`, `router-controllers`\r\n* Controllers: `stackdump`, `config`, `metrics`, `connected-routers`, `connected-peers`,\r\n  `cluster-config`, `router-messaging`, `terminator-costs`, `data-model-index`\r\n* Tunnelers: `stackdump`, `sdk`\r\n\r\n## Current Beta Features\r\n\r\nBeta features are still under development and are subject to change. They should\r\nbe usable in their released form. Though unlikely, there is a small chance they will \r\nbe removed. \r\n\r\n* Basic Permission System\r\n* Alert Events\r\n* Controller-Initiated Control Channel Dials\r\n\r\n## Component Updates and Bug Fixes\r\n\r\n* github.com/openziti/agent: [v1.0.31 -> v1.0.33](https://github.com/openziti/agent/compare/v1.0.31...v1.0.33)\r\n* github.com/openziti/channel/v4: [v4.2.28 -> v4.3.11](https://github.com/openziti/channel/compare/v4.2.28...v4.3.11)\r\n    * [Issue #242](https://github.com/openziti/channel/issues/242) - Reconnecting channel shouldn't allow changing ids\r\n    * [Issue #235](https://github.com/openziti/channel/issues/235) - Bump allowed hello message headers size to 16k from 4k\r\n    * [Issue #228](https://github.com/openziti/channel/issues/228) - Ensure that Underlay never return nil on MultiChannel\r\n    * [Issue #226](https://github.com/openziti/channel/issues/226) - Allow specifying a minimum number of underlays for a channel, regardless of underlay type\r\n    * [Issue #225](https://github.com/openziti/channel/issues/225) - Add ChannelCreated to the UnderlayHandler API to allow handlers to be initialized with the channel before binding\r\n    * [Issue #224](https://github.com/openziti/channel/issues/224) - Update the underlay dispatcher to allow unknown underlay types to fall through to the default\r\n    * [Issue #222](https://github.com/openziti/channel/issues/222) - Allow injecting the underlay type into messages\r\n\r\n* github.com/openziti/edge-api: [v0.26.47 -> v0.28.1](https://github.com/openziti/edge-api/compare/v0.26.47...v0.28.1)\r\n    * [Issue #175](https://github.com/openziti/edge-api/issues/175) - ctrlChanListeners should have x-omit-empty: false attribute\r\n    * [Issue #170](https://github.com/openziti/edge-api/issues/170) - Add preferredLeader flag to controllers\r\n    * [Issue #167](https://github.com/openziti/edge-api/issues/167) - Add ctrlChanListeners to router types\r\n    * [Issue #164](https://github.com/openziti/edge-api/issues/164) - Add permissions list to identity\r\n\r\n* github.com/openziti/foundation/v2: [v2.0.72 -> v2.0.91](https://github.com/openziti/foundation/compare/v2.0.72...v2.0.91)\r\n    * [Issue #472](https://github.com/openziti/foundation/issues/472) - Add support for multi-bit set/get to AtomicBitSet\r\n    * [Issue #464](https://github.com/openziti/foundation/issues/464) - Add support for -pre in versions\r\n    * [Issue #455](https://github.com/openziti/foundation/issues/455) - Correctly close goroutine pool when external close is signaled\r\n    * [Issue #452](https://github.com/openziti/foundation/issues/452) - Goroutine pool with a min worker count of 1 can drop to 0 workers due to race condition\r\n\r\n* github.com/openziti/identity: [v1.0.111 -> v1.0.129](https://github.com/openziti/identity/compare/v1.0.111...v1.0.129)\r\n    * [Issue #68](https://github.com/openziti/identity/issues/68) - Shutdown file watcher when stopping identity watcher\r\n\r\n* github.com/openziti/metrics: [v1.4.2 -> v1.4.5](https://github.com/openziti/metrics/compare/v1.4.2...v1.4.5)\r\n    * [Issue #58](https://github.com/openziti/metrics/issues/58) - Add GaugeFloat64 support\r\n    * [Issue #56](https://github.com/openziti/metrics/issues/56) - underlying resources of reference counted meters are not cleaned up when reference count hits zero\r\n\r\n* github.com/openziti/runzmd: [v1.0.80 -> v1.0.90](https://github.com/openziti/runzmd/compare/v1.0.80...v1.0.90)\r\n* github.com/openziti/sdk-golang: [v1.2.3 -> v1.7.0](https://github.com/openziti/sdk-golang/compare/v1.2.3...v1.7.0)\r\n    * [Issue #901](https://github.com/openziti/sdk-golang/issues/901) - Move xgress back to having retransmitter goroutine per-xgress\r\n    * [Issue #906](https://github.com/openziti/sdk-golang/issues/906) - Fix potential nil references on session service structs\r\n    * [Issue #897](https://github.com/openziti/sdk-golang/issues/897) - Allow xgress to use pull model for reads when appropriate\r\n    * [Issue #895](https://github.com/openziti/sdk-golang/issues/895) - Limit effect sudden rtt spikes can have on rtt moving average\r\n    * [Issue #902](https://github.com/openziti/sdk-golang/issues/902) - Inspect response message content types are mixed up\r\n    * [Issue #887](https://github.com/openziti/sdk-golang/issues/887) - Fix listener manager cleanup\r\n    * [Issue #886](https://github.com/openziti/sdk-golang/issues/886) - When controller is busy during service refresh, backoff and retry instead of falling back to full refresh\r\n    * [Issue #885](https://github.com/openziti/sdk-golang/issues/885) - Only compare relevant service fields when looking for changes\r\n    * [Issue #884](https://github.com/openziti/sdk-golang/issues/884) - Add deadline for bind establishment\r\n    * [Issue #883](https://github.com/openziti/sdk-golang/issues/883) - Router level listener can be left open if multi-listener closes during listener establishment\r\n    * [Issue #877](https://github.com/openziti/sdk-golang/issues/877) - Handle differences in xgress eof/end-of-circuit handling by adding a capabilities exchange\r\n    * [Issue #832](https://github.com/openziti/sdk-golang/issues/832) - Fuzz session refresh timers\r\n    * [Issue #879](https://github.com/openziti/sdk-golang/issues/879) - Return the connId in inspect response\r\n    * [Issue #878](https://github.com/openziti/sdk-golang/issues/878) - Fix responses from rx goroutines\r\n    * [Issue #874](https://github.com/openziti/sdk-golang/issues/874) - Add inspect support at the context level\r\n    * [Issue #871](https://github.com/openziti/sdk-golang/issues/871) - Make SDK better at sticking to MaxTerminator terminators\r\n    * [Issue #708](https://github.com/openziti/sdk-golang/issues/708) - Support for Go's built-in context in Dial methods\r\n    * [Issue #860](https://github.com/openziti/sdk-golang/issues/860) - Make the dialing identity's id and name available on dialed connections\r\n    * [Issue #857](https://github.com/openziti/sdk-golang/issues/857) - Use new error code and retry hints to correctly react to terminator errors\r\n    * [Issue #847](https://github.com/openziti/sdk-golang/issues/847) - Ensure the initial version check succeeds, to ensure we don't legacy sessions on ha or oidc-enabled controllers\r\n    * [Issue #824](https://github.com/openziti/sdk-golang/pull/824) - release notes and hard errors on no TOTP handler breaks partial auth events\r\n    * [Issue #818](https://github.com/openziti/sdk-golang/issues/818) - Full re-auth should not clear services list, as that breaks the on-change logic\r\n    * [Issue #817](https://github.com/openziti/sdk-golang/issues/817) - goroutines can get stuck when iterating over randomized HA controller list\r\n    * [Issue #736](https://github.com/openziti/sdk-golang/issues/736) - Migrate from github.com/mailru/easyjson\r\n    * [Issue #813](https://github.com/openziti/sdk-golang/issues/813) - SDK doesn't stop close listener when it detects that a service being hosted gets deleted\r\n    * [Issue #811](https://github.com/openziti/sdk-golang/issues/811) - Credentials are lost when explicitly set\r\n    * [Issue #807](https://github.com/openziti/sdk-golang/issues/807) - Don't send close from rxer to avoid blocking\r\n    * [Issue #800](https://github.com/openziti/sdk-golang/issues/800) - Tidy create service session logging\r\n\r\n* github.com/openziti/secretstream: [v0.1.39 -> v0.1.49](https://github.com/openziti/secretstream/compare/v0.1.39...v0.1.49)\r\n* github.com/openziti/transport/v2: [v2.0.188 -> v2.0.215](https://github.com/openziti/transport/compare/v2.0.188...v2.0.215)\r\n    * [Issue #31](https://github.com/openziti/transport/issues/31) - ipv6 Transport Address Parsing\r\n    * [Issue #149](https://github.com/openziti/transport/issues/149) - Archive transwarp code\r\n\r\n* github.com/openziti/xweb/v3: [v2.3.4 -> v3.0.4](https://github.com/openziti/xweb/compare/v2.3.4...v3.0.4)\r\n    * [Issue #32](https://github.com/openziti/xweb/issues/32) - watched identities sometimes don't reload when changed\r\n\r\n* github.com/openziti/go-term-markdown: v1.0.1 (new)\r\n* github.com/openziti/ziti/v2: [v1.6.8 -> v2.0.0](https://github.com/openziti/ziti/compare/v1.6.8...v2.0.0)\r\n    * [Issue #3860](https://github.com/openziti/ziti/issues/3860) - Router data model index resets to 0 on controller restart\r\n    * [Issue #3857](https://github.com/openziti/ziti/issues/3857) - Router first-party cert check should include intermediates from the TLS peer chain\r\n    * [Issue #3855](https://github.com/openziti/ziti/issues/3855) - Filter current api session certs list by current api session\r\n    * [Issue #3838](https://github.com/openziti/ziti/issues/3838) - List controllers on management API has incorrect permissions check\r\n    * [Issue #3837](https://github.com/openziti/ziti/issues/3837) - Create db snapshot with path has incorrect permissions check\r\n    * [Issue #3846](https://github.com/openziti/ziti/issues/3846) - OIDC token binds client cert during non-cert auth, causes PoP failures\r\n    * [Issue #3809](https://github.com/openziti/ziti/issues/3809) - Support CSR submission during OIDC authentication for session-bound certificates\r\n    * [Issue #3830](https://github.com/openziti/ziti/issues/3830) - statemanager is holding on to edge connections and they're never getting cleared\r\n    * [Issue #3824](https://github.com/openziti/ziti/issues/3824) - Allow calling inspect using the IPC agent on the controller, router and go tunnel\r\n    * [Issue #2049](https://github.com/openziti/ziti/issues/2049) - The ziti agent command should have a controller connection status\r\n    * [Issue #3784](https://github.com/openziti/ziti/issues/3784) - Fix link registry race condition on reporting links on reconnect\r\n    * [Issue #3734](https://github.com/openziti/ziti/issues/3734) - Enforce client certificate proof-of-possession on controller REST API for OIDC sessions\r\n    * [Issue #3806](https://github.com/openziti/ziti/issues/3806) - Expose OpenZiti-specific login and MFA endpoints in the OIDC discovery document\r\n    * [Issue #3818](https://github.com/openziti/ziti/issues/3818) - Filtering policies by keywords `Dial` and `Bind` doesn't work\r\n    * [Issue #3816](https://github.com/openziti/ziti/issues/3816) - Support multiple upstream DNS providers in ziti tunnel and ER/T\r\n    * [Issue #3699](https://github.com/openziti/ziti/issues/3699) - Consolidate CLI edge and fabric commands in top level create/update/delete/list/login commands\r\n    * [Issue #3788](https://github.com/openziti/ziti/issues/3788) - OIDC Endpoints return 400 Bad Request instead of underlying error\r\n    * [Issue #3680](https://github.com/openziti/ziti/issues/3680) - adds revocation control to CLI and Management API\r\n    * [Issue #3717](https://github.com/openziti/ziti/issues/3717) - Generic error message for specific error\r\n    * [Issue #3543](https://github.com/openziti/ziti/issues/3543) - New Circuit Failure code for sockets not available\r\n    * [Issue #3364](https://github.com/openziti/ziti/issues/3364) - Make no such host error specific\r\n    * [Issue #2888](https://github.com/openziti/ziti/issues/2888) - New specific Error code for port not allowed\r\n    * [Issue #2859](https://github.com/openziti/ziti/issues/2859) - Create specific error code for DNS failed resolution\r\n    * [Issue #1580](https://github.com/openziti/ziti/issues/1580) - Invalid link destination should have a specific error code\r\n    * [Issue #3706](https://github.com/openziti/ziti/issues/3706) - Increase link payload/ack queue sizes and make them configurable\r\n    * [Issue #3778](https://github.com/openziti/ziti/issues/3778) - SetRouterDataModel can deadlock in the router\r\n    * [Issue #3777](https://github.com/openziti/ziti/issues/3777) - With the new circuit reserve, we can have circuits with no path in the controller circuit set, which can cause panics\r\n    * [Issue #3770](https://github.com/openziti/ziti/issues/3770) - Update Token Requests Should Close Channel Connections If Invalid\r\n    * [Issue #3762](https://github.com/openziti/ziti/issues/3762) - Revocations not included in full router data model state\r\n    * [Issue #3757](https://github.com/openziti/ziti/issues/3757) - Mesh peer signing cert from header is overwritten by TLS underlay cert\r\n    * [Issue #3756](https://github.com/openziti/ziti/issues/3756) - TLS handshake rate limiter timeout check reads from wrong config scope\r\n    * [Issue #3755](https://github.com/openziti/ziti/issues/3755) - commandHandler config read from wrong scope\r\n    * [Issue #3754](https://github.com/openziti/ziti/issues/3754) - dialFailed drops applyFailed parameter, preventing duplicate link retry jitter\r\n    * [Issue #3753](https://github.com/openziti/ziti/issues/3753) - SPIFFE trust domain prefix check has swapped HasPrefix arguments\r\n    * [Issue #3746](https://github.com/openziti/ziti/issues/3746) - The controller connect events control channel handler leaks a goroutine\r\n    * [Issue #3747](https://github.com/openziti/ziti/issues/3747) - Update controller peer error marshalling for app code changes\r\n    * [Issue #3721](https://github.com/openziti/ziti/issues/3721) - Add CreateCircuitV3 to controller\r\n    * [Issue #3719](https://github.com/openziti/ziti/issues/3719) - Allow binding specific inspects to pass through to xgress listener implementations\r\n    * [Issue #3696](https://github.com/openziti/ziti/issues/3696) - oidc provider is non-deterministic for wildcard certs\r\n    * [Issue #3681](https://github.com/openziti/ziti/issues/3681) - coalesce OIDC JWT revocations to reduce controller write pressure\r\n    * [Issue #3683](https://github.com/openziti/ziti/issues/3683) - add fablab test for testing flow control changes over a longer term\r\n    * [Issue #3673](https://github.com/openziti/ziti/issues/3673) - revocation build-up in db and rdm\r\n    * [Issue #3674](https://github.com/openziti/ziti/issues/3674) - Update to Go 1.26\r\n    * [Issue #3496](https://github.com/openziti/ziti/issues/3496) - MFA TOTP Enrollment During OIDC Authentication Does Not Work\r\n    * [Issue #3609](https://github.com/openziti/ziti/issues/3609) - Stabilize terminator creation test for 2.0\r\n    * [Issue #3648](https://github.com/openziti/ziti/issues/3648) - tunnel: myCopy logs router ID as circuitId, causing misleading debug output\r\n    * [Issue #3658](https://github.com/openziti/ziti/issues/3658) - Raft cluster join fails with \"hello message too big\" when using long hostnames\r\n    * [Issue #3626](https://github.com/openziti/ziti/issues/3626) - controller: overlay bind point produces malformed URL in /versions apiBaseUrls\r\n    * [Issue #3635](https://github.com/openziti/ziti/issues/3635) - Allow controllers to dial routers to support more topologies\r\n    * [Issue #3607](https://github.com/openziti/ziti/issues/3607) - linux installer not upgradable from v1\r\n    * [Issue #3650](https://github.com/openziti/ziti/issues/3650) - Reroute doesn't proactively clean up orphaned route entries\r\n    * [Issue #3571](https://github.com/openziti/ziti/issues/3571) - Ensure 2.0 backwards compatibility with 1.6 and 1.5 using the smoketest\r\n    * [Issue #3636](https://github.com/openziti/ziti/issues/3636) - Adaptive rate limiter should use success rate rather than queue position\r\n    * [Issue #3600](https://github.com/openziti/ziti/issues/3600) - Add a preferredLeader flag, allow selected nodes to be preferred for raft leader, if they're available\r\n    * [Issue #3642](https://github.com/openziti/ziti/issues/3642) - Use xgress_common.Connection type for xgress_transport and xgress_proxy\r\n    * [Issue #3597](https://github.com/openziti/ziti/issues/3597) - Enable OIDC API by default\r\n    * [Issue #3624](https://github.com/openziti/ziti/issues/3624) - Multi-underlay control channel doesn't correctly handle lack of group secret on non-grouped underlays\r\n    * [Issue #3613](https://github.com/openziti/ziti/issues/3613) - Initializing cluster from existing db using `db:` config settings results in panic\r\n    * [Issue #3620](https://github.com/openziti/ziti/issues/3620) - Controller control channel heartbeats config parsing was erroneously nested under options parsing\r\n    * [Issue #3619](https://github.com/openziti/ziti/issues/3619) - Router connect events full sync interval was using min/max values meant for the batch interval\r\n    * [Issue #3618](https://github.com/openziti/ziti/issues/3618) - Router interfaceDiscovery.minReportInterval value was being set to checkInterval\r\n    * [Issue #3617](https://github.com/openziti/ziti/issues/3617) - Transit router disabled flag not passed through raft command structure\r\n    * [Issue #3333](https://github.com/openziti/ziti/issues/3333) - Updb user-lockout triggered by successful login attempts\r\n    * [Issue #3599](https://github.com/openziti/ziti/issues/3599) - Add gap detection and handling to router data model\r\n    * [Issue #3356](https://github.com/openziti/ziti/issues/3356) - Add WWW-Authenticate Headers\r\n    * [Issue #3074](https://github.com/openziti/ziti/issues/3074) - Dynamic cost range is too limited\r\n    * [Issue #3558](https://github.com/openziti/ziti/issues/3558) - terminator cost increased on egress dial success, not on circuit completion\r\n    * [Issue #3556](https://github.com/openziti/ziti/issues/3556) - global circuit costs not cleared when terminator is deleted\r\n    * [Issue #3557](https://github.com/openziti/ziti/issues/3557) - costing calculation for the weighted terminator selection strategy  is incorrect\r\n    * [Issue #2512](https://github.com/openziti/ziti/issues/2512) - Return circuit ID and error in dial failures\r\n    * [Issue #3569](https://github.com/openziti/ziti/issues/3569) - Version 2.0+ routers should not connect to controllers which do not support JWT formatted legacy sessions\r\n    * [Issue #3565](https://github.com/openziti/ziti/issues/3565) - Link dialer save 'is first conn' true, so all dials claim to be first, causing potential race condition\r\n    * [Issue #3575](https://github.com/openziti/ziti/issues/3575) - OIDC token endpoint code bugs possibly resulting in panics/eof errors\r\n    * [Issue #3550](https://github.com/openziti/ziti/issues/3550) - Support multi-underlay control channels\r\n    * [Issue #3535](https://github.com/openziti/ziti/issues/3535) - Remove the legacy xgress_edge_tunnel implementation\r\n    * [Issue #3547](https://github.com/openziti/ziti/issues/3547) - Add support for sending the dialing identity id and name to the hosting sdk\r\n    * [Issue #3541](https://github.com/openziti/ziti/issues/3541) - Remove option to disable the router data model in the controller\r\n    * [Issue #3540](https://github.com/openziti/ziti/issues/3540) - Handle UDP difference between proxy and tproxy implementations\r\n    * [Issue #3527](https://github.com/openziti/ziti/issues/3527) - ER/T UDP tunnels keep closed connections for 30s, preventing potential new good connections in that time\r\n    * [Issue #3526](https://github.com/openziti/ziti/issues/3526) - ER/T half-close logic is incorrect\r\n    * [Issue #3524](https://github.com/openziti/ziti/issues/3524) - Provide more error context to SDKs for terminator errors\r\n    * [Issue #3509](https://github.com/openziti/ziti/issues/3509) - Enforce policy on the router for oidc sessions, by closing open circuits and terminators when service access is lost\r\n    * [Issue #3531](https://github.com/openziti/ziti/issues/3531) - Remove created/updated/deleted terminator events. Obsoleted by entity change events.\r\n    * [Issue #3532](https://github.com/openziti/ziti/issues/3532) - Removed deprecated create identity <type> subcommands\r\n    * [Issue #3521](https://github.com/openziti/ziti/issues/3521) - Cleanup CLI to remove calls to os.Exit to be embed friendlier\r\n    * [Issue #3516](https://github.com/openziti/ziti/issues/3516) - Remove support for create terminator v1\r\n    * [Issue #3512](https://github.com/openziti/ziti/issues/3512) - Remove legacy link management code from the controller\r\n    * [Issue #3511](https://github.com/openziti/ziti/issues/3511) - router proxy mode fails to resolve interface if binding is 0.0.0.0\r\n    * [Issue #3503](https://github.com/openziti/ziti/issues/3503) - Allow routers to request current cluster membership information\r\n    * [Issue #3501](https://github.com/openziti/ziti/issues/3501) - Get cluster membership information from raft directly, rather than trying to cache it in the DB\r\n    * [Issue #3500](https://github.com/openziti/ziti/issues/3500) - Set a router data model timeline when initializing a new HA setup, rather than letting it stay blank\r\n    * [Issue #3504](https://github.com/openziti/ziti/issues/3504) - Reduce router data model full state updates\r\n    * [Issue #3492](https://github.com/openziti/ziti/pull/3492) - Bump openziti/ziti-console-assets from 3.12.9 to 4.0.0 in /dist/docker-images/ziti-controller in the all group\r\n    * [Issue #3484](https://github.com/openziti/ziti/issues/3484) - router ctrl channel handler for handling cluster changes has an initialization race condition\r\n    * [Issue #3477](https://github.com/openziti/ziti/issues/3477) - Optionally enable model changes triggered by login to be non-blocking and to be droppable if the system is under load\r\n    * [Issue #3473](https://github.com/openziti/ziti/issues/3473) - Enable tls handshake rate limiter by default and tweak default values.\r\n    * [Issue #3471](https://github.com/openziti/ziti/issues/3471) - Go tunneler is ignoring host config MaxConnections\r\n    * [Issue #3469](https://github.com/openziti/ziti/issues/3469) - Only send model updates on resubscribe if the RDM index has advanced\r\n    * [Issue #2573](https://github.com/openziti/ziti/issues/2573) - An edge router in a tight restart loop causes a resource leak on routers to which it connects.\r\n    * [Issue #3430](https://github.com/openziti/ziti/issues/3430) - Add permissions list to identity\r\n    * [Issue #2109](https://github.com/openziti/ziti/issues/2109) - Add Edge Management Read Only Capability\r\n    * [Issue #3435](https://github.com/openziti/ziti/issues/3435) - Add edge management API permissions by entity type and action\r\n    * [Issue #3441](https://github.com/openziti/ziti/issues/3441) - Update router connection tracker to interrogate active connections\r\n    * [Issue #3451](https://github.com/openziti/ziti/issues/3451) - ci - compare only stable releases when promoting\r\n    * [Issue #3437](https://github.com/openziti/ziti/issues/3437) - SDK OIDC token updates to routers should return an error if invalid\r\n    * [Issue #3444](https://github.com/openziti/ziti/issues/3444) - Check api session types during OIDC token updates to avoid nil reference on mixed-auth identities\r\n    * [Issue #3348](https://github.com/openziti/ziti/issues/3348) - Unable to clear/reset the \"tags\" property on an entity to an empty object\r\n    * [Issue #3452](https://github.com/openziti/ziti/issues/3452) - `ziti agent cluster add` has bad behavior if the add address doesn't match the advertise address\r\n    * [Issue #3410](https://github.com/openziti/ziti/issues/3410) - Consolidate fabric REST API code with edge management and edge client code\r\n    * [Issue #3425](https://github.com/openziti/ziti/issues/3425) - RDM not properly responding to tunneler enabled flag changes\r\n    * [Issue #3420](https://github.com/openziti/ziti/issues/3420) - The terminator id cache uses the same id for all terminators in a host.v2 config, resulting in a single terminator\r\n    * [Issue #3419](https://github.com/openziti/ziti/issues/3419) - When using the router data model, precedence specified on the per-service identity mapping are incorrectly interpreted\r\n    * [Issue #3318](https://github.com/openziti/ziti/issues/3318) - Terminator creation seems to slow exponentially as the number of terminators rises from 10k to 20k to 40k\r\n    * [Issue #3407](https://github.com/openziti/ziti/issues/3407) - The CLI doesn't properly pass JWT authentication information to websocket endpoints\r\n    * [Issue #3359](https://github.com/openziti/ziti/issues/3359) - Ensure router data model subscriptions have reasonable performance and will scale\r\n    * [Issue #3354](https://github.com/openziti/ziti/issues/3354) - SDK/ENV Info from SDKs is not distributed in HA\r\n    * [Issue #3381](https://github.com/openziti/ziti/issues/3381) - the fabric service REST apis are missing the maxIdleTime property\r\n    * [Issue #3382](https://github.com/openziti/ziti/issues/3382) - Legacy service sessions generated pre-1.7.x are incompatible with v1.7.+ and need to be cleared\r\n    * [Issue #3339](https://github.com/openziti/ziti/issues/3339) - get router ctrl.endpoint from ctrls claim in JWT\r\n    * [Issue #3378](https://github.com/openziti/ziti/issues/3378) - login with file stopped working\r\n    * [Issue #3349](https://github.com/openziti/ziti/issues/3349) - UPDB OIDC login returns wrong content type\r\n    * [Issue #2324](https://github.com/openziti/ziti/issues/2324) - Add Ext-JWT/OIDC enrollment\r\n    * [Issue #3346](https://github.com/openziti/ziti/issues/3346) - Fix confusing attempt logging\r\n    * [Issue #3337](https://github.com/openziti/ziti/issues/3337) - Router reports \"no xgress edge forwarder for circuit\"\r\n    * [Issue #3345](https://github.com/openziti/ziti/issues/3345) - Clean up connect events tests and remove global XG registry\r\n    * [Issue #3264](https://github.com/openziti/ziti/issues/3264) - Allow routers to generate alert events in cases of service misconfiguration\r\n    * [Issue #3321](https://github.com/openziti/ziti/issues/3321) - Health Check API missing base path on discovery endpoint\r\n    * [Issue #3323](https://github.com/openziti/ziti/issues/3323) - router/tunnel static services fail to bind unless new param protocol is defined\r\n    * [Issue #3309](https://github.com/openziti/ziti/issues/3309) - Detect link connections meant for another router\r\n    * [Issue #3286](https://github.com/openziti/ziti/issues/3286) - edge-api binding doesn't have the correct path on discovery endpoints\r\n    * [Issue #3297](https://github.com/openziti/ziti/issues/3297) - stop promoting hotfixes downstream\r\n    * [Issue #3295](https://github.com/openziti/ziti/issues/3295) - make ziti tunnel service:port pairs optional\r\n    * [Issue #3291](https://github.com/openziti/ziti/issues/3291) - replace decommissioned bitnami/kubectl\r\n    * [Issue #3277](https://github.com/openziti/ziti/issues/3277) - Router can deadlock on closing a connection if the incoming data channel is full\r\n    * [Issue #3269](https://github.com/openziti/ziti/issues/3269) - Add host-interfaces config type\r\n    * [Issue #3258](https://github.com/openziti/ziti/issues/3258) - Add config type proxy.v1 so proxies can be defined dynamically for the ER/T\r\n    * [Issue #3259](https://github.com/openziti/ziti/issues/3259) - Interfaces config type not added due to wrong name\r\n    * [Issue #3265](https://github.com/openziti/ziti/issues/3265) - Forwarding errors should log at debug, since they are usual part of circuit teardown\r\n    * [Issue #3261](https://github.com/openziti/ziti/issues/3261) - ER/T dialed xgress connections may only half-close when peer is fully closed\r\n\r\n\r\n","reactions":{"url":"https://api.github.com/repos/openziti/ziti/releases/326144067/reactions","total_count":5,"+1":5,"-1":0,"laugh":0,"hooray":0,"confused":0,"heart":0,"rocket":0,"eyes":0},"mentions_count":3},{"url":"https://api.github.com/repos/openziti/ziti/releases/324459840","assets_url":"https://api.github.com/repos/openziti/ziti/releases/324459840/assets","upload_url":"https://uploads.github.com/repos/openziti/ziti/releases/324459840/assets{?name,label}","html_url":"https://github.com/openziti/ziti/releases/tag/v2.0.0-pre15","id":324459840,"author":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"node_id":"RE_kwDODVFMN84TVt1A","tag_name":"v2.0.0-pre15","target_commitish":"main","name":"v2.0.0-pre15","draft":false,"immutable":false,"prerelease":true,"created_at":"2026-05-18T17:40:55Z","updated_at":"2026-05-18T17:55:48Z","published_at":"2026-05-18T17:55:48Z","assets":[{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/423496583","id":423496583,"node_id":"RA_kwDODVFMN84ZPguH","name":"checksums.sha256.txt","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"text/plain; charset=utf-8","state":"uploaded","size":798,"digest":"sha256:fa843c43651c86977342b806a729de8fcc0810f51d9af565a0e6a937783c015b","download_count":4,"created_at":"2026-05-18T17:55:45Z","updated_at":"2026-05-18T17:55:46Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre15/checksums.sha256.txt"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/423496577","id":423496577,"node_id":"RA_kwDODVFMN84ZPguB","name":"sbom-v2.0.0-pre15.spdx.json","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/json","state":"uploaded","size":988218,"digest":"sha256:7a1df1b58809077868dd108c634b3100dd45a188166fb910d80027f2690fba98","download_count":3,"created_at":"2026-05-18T17:55:45Z","updated_at":"2026-05-18T17:55:45Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre15/sbom-v2.0.0-pre15.spdx.json"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/423496578","id":423496578,"node_id":"RA_kwDODVFMN84ZPguC","name":"source-v2.0.0-pre15.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":4004793,"digest":"sha256:f00d23bfc4a3ac5ba040cca15cde760e97a729461b877344a0a8832553357635","download_count":5,"created_at":"2026-05-18T17:55:45Z","updated_at":"2026-05-18T17:55:46Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre15/source-v2.0.0-pre15.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/423496555","id":423496555,"node_id":"RA_kwDODVFMN84ZPgtr","name":"ziti-darwin-amd64-2.0.0-pre15.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":54798156,"digest":"sha256:8b57b9361fc7e49f8c943eb921170fe15a28b44085940e380bf6a61436a371a7","download_count":7,"created_at":"2026-05-18T17:55:41Z","updated_at":"2026-05-18T17:55:45Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre15/ziti-darwin-amd64-2.0.0-pre15.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/423496556","id":423496556,"node_id":"RA_kwDODVFMN84ZPgts","name":"ziti-darwin-arm64-2.0.0-pre15.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":51059945,"digest":"sha256:d676d8a10df80a24fd747cdbca9b7cc16026988ae488d5ed705c67b8552dc144","download_count":5,"created_at":"2026-05-18T17:55:41Z","updated_at":"2026-05-18T17:55:45Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre15/ziti-darwin-arm64-2.0.0-pre15.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/423496552","id":423496552,"node_id":"RA_kwDODVFMN84ZPgto","name":"ziti-linux-amd64-2.0.0-pre15.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":56113469,"digest":"sha256:73c5b6efc7931977439e2842b24289a13579e0fb14b8986290a41f9879a2ab99","download_count":29,"created_at":"2026-05-18T17:55:41Z","updated_at":"2026-05-18T17:55:45Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre15/ziti-linux-amd64-2.0.0-pre15.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/423496553","id":423496553,"node_id":"RA_kwDODVFMN84ZPgtp","name":"ziti-linux-arm-2.0.0-pre15.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":52510914,"digest":"sha256:590a6a689b56de3b9a464d545ef810f3badc69c8eafdcac140813876b0ba85c3","download_count":6,"created_at":"2026-05-18T17:55:41Z","updated_at":"2026-05-18T17:55:45Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre15/ziti-linux-arm-2.0.0-pre15.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/423496575","id":423496575,"node_id":"RA_kwDODVFMN84ZPgt_","name":"ziti-linux-arm64-2.0.0-pre15.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":52546590,"digest":"sha256:0db8b91e6b0e983c0928d799c752af187bedf38c1812eda1d2af7fdb86e056a2","download_count":7,"created_at":"2026-05-18T17:55:45Z","updated_at":"2026-05-18T17:55:47Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre15/ziti-linux-arm64-2.0.0-pre15.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/423496554","id":423496554,"node_id":"RA_kwDODVFMN84ZPgtq","name":"ziti-windows-amd64-2.0.0-pre15.zip","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/zip","state":"uploaded","size":45357488,"digest":"sha256:1c9357371be8adf85576c80a4d69ff699b245f116b192bcfdf337ba98ee0808a","download_count":9,"created_at":"2026-05-18T17:55:41Z","updated_at":"2026-05-18T17:55:44Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre15/ziti-windows-amd64-2.0.0-pre15.zip"}],"tarball_url":"https://api.github.com/repos/openziti/ziti/tarball/v2.0.0-pre15","zipball_url":"https://api.github.com/repos/openziti/ziti/zipball/v2.0.0-pre15","body":"# Release 2.0.0\n\n## What's New\n\nThis is the next major version release of OpenZiti, following the 1.0 release in April 2024.\nOf particular note is that HA controllers are now considered ready for general use.\nThis release also introduces a new permissions model, OIDC/JWT token-based enrollment, \nclustering performance improvements, and a number of other features and fixes. Because \nsome of these changes are not backwards compatible with older routers, we're marking this \nas a major version bump.\n\n### HA Controllers are now considered ready for general use\n\nThis is a pretty big milestone and marks the completion of work that's been ongoing for a couple of years.\nThe HA work has brought with it some notable changes. Authentication now uses JWTs by default. Using JWTs\nmeans the controllers don't need to store session and propagate them to the routers. This removes a bottleneck\nfrom the network and allows the load to be more easily distributed among controllers and routers.\n\nTo support distributed authentication, the routers now get a bespoke version of the data model. In addition\nto enabling distributed authentication this will allow us to remove the need for service polling and further \nreduce the load on controllers in the future.\n\n### Router Compatibility\n\nRelated to the JWT work, routers with version 2.+ will only work with controllers that are version 2.+. This means\nto upgrade your network, controllers should be upgraded first. Routers can then be upgraded individually.\n\n2.x routers should still work fine with older router versions.\n\nWe try very hard to avoid breaking changes like this, but sometimes the engineering trade-offs lead there. This change\nwas first made in the 1.7 release. That release has not been marked stable, and we have no plans to do so, because\nof the backwards incompatibility. \n\nIf you need to support in the 1.6.12+ range, see the section \"OIDC enabled by default\".\n\n### Release Policy and LTS\n\nAlongside this release we're publishing a formal [Release Policy](./RELEASE_POLICY.md). The goal is to give\noperators a predictable upgrade cadence and a clear answer to \"is my version still supported?\"\n\nThe short version:\n\n* **2.0 is the current LTS (N).** It will receive security fixes and critical bug fixes.\n* **1.6 is now the maintenance LTS (N-1).** It will receive security fixes and fixes for critical production\n  defects only.\n* 2.0 remains the current LTS until the next LTS is cut, approximately a year from now. At that point 2.0 will\n  move to maintenance and **1.6 will reach end of life and no longer be supported**.\n* Two active LTS versions at any time means you're always within a two-year support window.\n\nA few things worth calling out from the policy that operators planning upgrades should know:\n\n* Sequential upgrades (N-1 → N) are the only tested and guaranteed upgrade path. Skipping LTS generations\n  carries no compatibility guarantee, so 1.6 users should plan their move to 2.0 before 1.6 goes EOL.\n* The latest released versions of all supported SDKs and tunnelers (Go, C, Java, Swift, Python, Node.js,\n  .NET/C#, Desktop Edge, mobile clients) are guaranteed to work against any active LTS controller/router.\n  The SDK/tunneler versions current at the time an LTS was cut also remain compatible with subsequent patch\n  releases of that same LTS, so deployments that pin their SDK won't be broken by a controller/router patch\n  within the same LTS generation.\n* Releases between LTS cuts (the \"Latest Development\" track) continue to ship features and fixes, but carry\n  no LTS guarantees.\n* Feature backports to LTS are exceptional and require explicit maintainer approval. They are never applied\n  to the maintenance LTS.\n\nSee [RELEASE_POLICY.md](./RELEASE_POLICY.md) for the full lifecycle, support scope per phase, testing\nguarantees, and version compatibility details.\n\n### New Permissions Model (BETA)\n\nAs one feature goes out of beta, another arrives into beta. This release introduces a new permissions system\nfor more fine grained control to the management API. It's not expected to change, but may do so based on feedback\nfrom users.\n\n### CLI Reorganization\n\nThe `ziti` CLI has been reorganized to consolidate commands that were previously \nspread across `ziti edge` and `ziti fabric` into unified top-level commands. Entity \nmanagement is now available directly via `ziti create`, `ziti delete`, `ziti list`, \nand `ziti update`. Session management has been simplified with top-level `ziti login`. \nThe existing `ziti edge` and `ziti fabric` command trees remain available.\n\n**Breaking change:** `ziti create ca` now creates a Ziti edge Certificate Authority \n(previously it created a PKI CA). PKI CA creation is still available via `ziti pki create ca`.\n\nSee [CLI Reorganization Details](#cli-reorganization-details) for the full command mapping.\n\n### Updated Release Process\n\nWe have moved to creating `-preN` releases for major and minor versions. This way we can put out release candidates,\nor feature previews and put them through internal testing and let interested folks from the community try them out.\nThen, when we're ready, we can run the full validation suite against the last pre-release and retag it. \n\nPatch releases won't have `-preN` and should contain only high priority bug fixes.\n\n### Deprecation Cleanup\n\nSince we already have a breaking change, we're removing some other backwards compatibility code.\n\n* Controller managed links \n    * Router managed links were introduced in v0.30.0. \n    * If you're upgrading from an older versions, you'll want to upgrade to the latest 1.x release before jumping to 2.x\n    * Github tracking issue: https://github.com/openziti/ziti/issues/3512\n* `ziti edge create identity <type>`\n    * Identity types other than router were removed in v0.30.2\n    * The `type` can be dropped from the CLI command\n    * Github tracking issue: https://github.com/openziti/ziti/issues/3532\n* Terminator create/update/delete events\n    * These have been superseded by entity change events, which also have create/update/delete events for terminators\n    * Entity change events were introduced in v0.28.0\n    * Github tracking issue: https://github.com/openziti/ziti/issues/3531\n* `xgress_edge_tunnel` v1\n    * This is the first implementation of the tunneler in edge-router code (ER/T) which used legacy api sessions and services\n    * The v2 version uses the router data model and was introduced in v0.30.x\n    * Github tracking issue: https://github.com/openziti/ziti/issues/3516\n* Service policy filter `type = 1` / `type = 2`\n    * Service policy list queries now expect the string form (`type = \"Dial\"`, `type = \"Bind\"`) matching the REST API\n    * The integer form was an undocumented side effect of the internal storage format and never worked with the documented filter names\n    * Github tracking issue: https://github.com/openziti/ziti/issues/3818\n\n### Generic SPA Hosting\n\nThe controller's web layer now supports hosting arbitrary single-page applications via a generic\n`binding: spa` API. Each `binding: spa` entry takes a `path` (which becomes the URL context root) and\na `location` (the directory to serve). Multiple SPAs can be registered side by side at different\ncontext roots. Example:\n\n```yaml\n- binding: spa\n  options:\n    path: zac\n    location: /opt/openziti/share/console\n    indexFile: index.html\n- binding: spa\n  options:\n    path: my-app\n    location: /opt/my-app\n    indexFile: index.html\n```\n\nThe previous `binding: zac` is preserved as a back-compat shim and continues to work without\nmodification, but emits a deprecation warning at startup. New deployments should prefer\n`binding: spa` with an explicit `path`. The legacy shim retains a global `/assets/*` URL capture so\nZAC bundles built with absolute asset paths keep working; new SPAs bound via `binding: spa` are\nexpected to use relative URLs (or be built with `<base href>` matching their `path`).\n\nThe SPA file-serving handler also gained defense-in-depth path-traversal checks (boundary-aware\nprefix matching plus a `filepath.Rel`-based containment check on every served file) so that a\ncrafted URL cannot escape the configured `location` even if the standard library's own protections\never change.\n\n### Legacy Session Deprecation\n\nOIDC sessions are now preferred. They are the default, or will become the default for SDKs and tunnelers. They are also required\nwhen running HA. Legacy API and service session are now deprecated and will be removed in the OpenZiti v3.0.0 release. \n\n### Additional Features\n\n* Controllers can now optionally bind APIs using an OpenZiti identity\n* `ziti edge login` now supports the `--network-identity` flag to authenticate and establish connections through the Ziti overlay network\n* `ziti edge login` now supports using a bearer token with `--token` for authentication. The token is expected to be\n  provided as just the JWT, not with the \"Bearer \" prefix\n* Identity configuration can now be loaded from files or environment variables for flexible deployment scenarios\n* Identities can now be provisioned just-in-time through OIDC/JWT token-based enrollment\n* Multiple model updates can now be in-flight at the same time, improving clustering performance\n* Authentication-related model updates can now be non-blocking and even dropped if the system is too busy\n* Routers now provide more error context to SDKs for terminator errors, enabling better retry behavior\n* New `proxy.v1` config type for dynamic service proxies (originally released in 1.7.0)\n* New alert event type for surfacing operational issues to network operators - Beta (originally released in 1.7.0)\n* New Azure Service Bus event sink for streaming controller events, contributed by @ffaraone (originally released in 1.7.0)\n* Bundled ZAC upgraded to 4.0\n* Build updated to Go 1.25\n* CLI cleaned up to remove calls to `os.Exit`, making it more friendly for embedding\n* OIDC is now enabled by default\n* Controller Edge APIs now return `WWW-Authenticate` response headers on `401 Unauthorized` responses, giving clients actionable information about which auth methods are accepted and what went wrong\n* HA Controllers can be marked as 'preferredLeader' via config\n* Dynamic cost range for smart routing expanded beyond the previous 64K limit\n* Dial failures now return the circuit ID and error information for easier debugging\n* Router-to-controller control channels now support multiple underlays with priority-based message routing\n* The dialing identity's ID and name are now forwarded to the hosting SDK\n* Controllers can now dial routers to establish control channels, enabling connectivity when routers are behind firewalls (Beta)\n* Refresh-token revocations are now batched and best-effort, removing the database/raft bottleneck on token refreshes\n* [OIDC discovery endpoint extensions](#oidc-discovery-endpoint-extensions) - OpenZiti-specific endpoint URLs in the OIDC discovery document\n* `ziti edge quickstart` now always runs in HA mode. The `ha` subcommand has been removed. Use\n  `ziti edge quickstart join` to add additional members to the cluster. Note: existing quickstart instances\n  are not compatible with the new HA-only mode and will need to be recreated.\n* The `--clustered` flag on `ziti create config controller` has been removed; the generated config is always\n  cluster-ready. If you have scripts passing `--clustered`, remove it.\n* [Connect events pool](#connect-events-pool) - fixes a goroutine leak when routers reconnect and ensures per-router event ordering\n* [Multiple DNS upstreams](#multiple-dns-upstreams) - the tunneler can now fan out recursive queries to several upstream resolvers in parallel\n* [OIDC CSR authentication](#oidc-csr-authentication) - identities can submit a CSR during OIDC authentication to obtain a session-bound certificate for mTLS channel communication\n\n## OIDC Discovery Endpoint Extensions\n\nThe OIDC discovery document (`/.well-known/openid-configuration`) now includes a vendor-specific\n`openziti_endpoints` field. This lets SDKs discover OpenZiti's custom login and MFA endpoints at\nruntime instead of hardcoding paths.\n\nThe field contains absolute URLs for each endpoint, derived from the issuer the client connected to:\n\n```json\n{\n  \"issuer\": \"https://controller.example.com:1280/oidc\",\n  \"authorization_endpoint\": \"https://controller.example.com:1280/oidc/authorize\",\n  \"token_endpoint\": \"https://controller.example.com:1280/oidc/oauth/token\",\n  \"...other standard OIDC fields...\",\n  \"openziti_endpoints\": {\n    \"password\":           \"https://controller.example.com:1280/oidc/login/password\",\n    \"cert\":               \"https://controller.example.com:1280/oidc/login/cert\",\n    \"ext_jwt\":            \"https://controller.example.com:1280/oidc/login/ext-jwt\",\n    \"totp\":               \"https://controller.example.com:1280/oidc/login/totp\",\n    \"totp_enroll\":        \"https://controller.example.com:1280/oidc/login/totp/enroll\",\n    \"totp_enroll_verify\": \"https://controller.example.com:1280/oidc/login/totp/enroll/verify\",\n    \"auth_queries\":       \"https://controller.example.com:1280/oidc/login/auth-queries\"\n  }\n}\n```\n\n| Key                | Method(s)   | Description                                       |\n|--------------------|-------------|---------------------------------------------------|\n| `password`         | POST        | Username/password authentication                  |\n| `cert`             | POST        | Client certificate authentication                 |\n| `ext_jwt`          | POST        | External JWT authentication                       |\n| `totp`             | POST        | TOTP code verification for MFA                    |\n| `totp_enroll`      | POST/DELETE | Start (POST) or delete (DELETE) TOTP enrollment   |\n| `totp_enroll_verify`| POST       | Verify a TOTP enrollment code                     |\n| `auth_queries`     | GET         | Retrieve pending authentication queries           |\n\nWhen the controller serves edge-oidc on multiple web servers, each discovery response reflects\nthe issuer (and port) the client connected to.\n\n## OIDC CSR Authentication\n\nIdentities that authenticate via non-certificate methods (password, external JWT) can now submit\na CSR during OIDC authentication to obtain a session-bound certificate. This enables mTLS channel\ncommunication with edge routers for identities that would otherwise have no client certificate.\n\nCertificate-authenticated identities can also submit a CSR to obtain an additional session\ncertificate alongside their authenticating certificate.\n\n### How It Works\n\nA CSR is submitted as part of the OIDC login credentials. The controller signs it and returns\nthe certificate PEM as a `session_cert` field in the token endpoint JSON response:\n\n```json\n{\n  \"access_token\": \"eyJ...\",\n  \"token_type\": \"bearer\",\n  \"refresh_token\": \"eyJ...\",\n  \"id_token\": \"eyJ...\",\n  \"expires_in\": 1800,\n  \"session_cert\": \"-----BEGIN CERTIFICATE-----\\nMII...\"\n}\n```\n\nThe issued certificate contains a SPIFFE ID binding it to the identity and API session:\n```\nspiffe://{trustDomain}/identity/{identityId}/apiSession/{apiSessionId}/apiSessionCertificate/{certId}\n```\n\n### CSR Submission Points\n\n| Token Endpoint Grant Type | CSR Source                            | Use Case             |\n|---------------------------|---------------------------------------|----------------------|\n| Authorization Code        | `csrPem` field in login POST body     | Initial cert issue   |\n| Refresh Token             | `csr_pem` form parameter              | Cert rotation        |\n| Token Exchange            | `csr_pem` form parameter              | Cert rotation        |\n\n### Certificate Fingerprint Claims\n\nThe access token JWT includes two related claims:\n\n- `z_cfs` (CertFingerprints): all certificate fingerprints valid for this session. Contains the\n  authenticating cert fingerprint (for cert auth) and/or the CSR-issued session cert fingerprint.\n- `z_acf` (AuthCertFingerprint): the authenticating certificate fingerprint, present only for\n  certificate-authenticated sessions.\n\nOn cert rotation via refresh or token exchange, `z_cfs` is rebuilt as the auth cert fingerprint\n(if present) plus the new CSR cert fingerprint. The previous session cert fingerprint is replaced.\n\n### Certificate Binding Verification\n\nWhen a token carries `z_cfs`, the controller enforces certificate binding on the refresh token\nand token exchange endpoints:\n\n- If `z_cfs` is non-empty, the TLS leaf certificate must match at least one fingerprint in\n  `z_cfs`. Requests without a matching certificate are rejected.\n- If `z_cfs` is empty, the controller falls back to SPIFFE ID verification, checking whether\n  the leaf certificate's SAN URI references the correct API session.\n\nA session that starts without `z_cfs` can transition to having it by submitting a CSR during\na refresh. Once `z_cfs` is present, it cannot be removed.\n\n### Controller Capability\n\nControllers advertise `OIDC_AUTH_WITH_CSR` in the `/version` capabilities list when OIDC is\nenabled. SDKs can check for this capability before attempting CSR submission.\n\n### Requirements\n\n- The CSR must be a valid PEM-encoded PKCS#10 certificate request. Invalid CSRs are rejected\n  with a 400 Bad Request error in OIDC error format.\n- The controller only uses the public key from the CSR. Subject, DNS names, IP addresses,\n  email addresses, and URI SANs in the CSR are ignored.\n- Issued certificates have a one-year lifetime.\n- Only the leaf certificate fingerprint is tracked in token claims. Intermediate certificates\n  in the TLS chain are not considered.\n\n## Connect Events Pool\n\nThe controller now uses per-router, single-worker goroutine pools to process identity\nconnect/disconnect events. Previously each router connection spawned a dedicated\ngoroutine that was never cleaned up on disconnect, leaking a goroutine per reconnect\ncycle. Under churn (e.g., chaos testing with hundreds of routers) this could accumulate\ntens of thousands of leaked goroutines and destabilize the controller.\n\nUsing a single-worker pool per router also ensures that events from the same router are\nalways processed in FIFO order. Previously, a shared multi-worker pool could process a\nfull-state sync after a newer incremental event from the same router, causing identities\nto be incorrectly marked as disconnected.\n\nThe pool is configurable in the controller config file:\n\n```yaml\nconnectEvents:\n  queueSize: 5    # per-router work queue depth (default: 5)\n  idleTime:  30s  # worker idle timeout before exit (default: 30s)\n```\n\nThe defaults are suitable for most deployments. Each router's worker starts on demand\nand exits after the idle timeout, so no goroutines are held when there is no work.\n\nNote: the `minWorkers` and `maxWorkers` settings have been removed. Each router's pool\nis fixed at one worker for correctness.\n\n## Community Contributors\n\nThank you to the following community members for their contributions:\n\n* @ffaraone - Azure Service Bus event sink\n* @dmuensterer - OIDC token refresh fixes\n* @nenkoru - Controller isleader health check endpoint\n* Jan Starkl - UPDB auth attempts fix\n* Mamy Ratsimbazafy - uint16 port range fix\n\n## Basic Permission System (BETA)\n\nAdded a basic permission system that allows more control over identity access to controller management API operations. \nThis replaces the previous binary admin/non-admin model with a more flexible permission system.\n\n**NOTE:** This feature is in BETA, primarily so we can get feedback on which permissions make sense. The implementation is unlikely to change\nbut the set of exposed permissions may grow, shrink or change based on user feedback. \n\n### Permission Model\n\nThe permission system supports three levels of authorization:\n\n  1. **Global Permissions**: System-wide access levels\n     - `admin` - Full access to all operations. This is still controlled by the `isAdmin` flag on identity\n     - `admin_readonly` - Read-only access to all resources except debugging facilities inspect and validate\n\n  2. **Entity-Level Permissions**: Full CRUD access to specific entity types\n     - Granting an entity-level permission (e.g., `service`) provides complete create, read, update, and delete access for that entity type\n\n  3. **Action-Level Permissions**: Specific operation access on entity types\n     - Fine-grained control using the pattern `<entity>.<action>` (e.g., `service.read`, `identity.update`)\n     - Supports `create`, `read`, `update`, and `delete` actions per entity type\n\n### Supported Entity Permissions\n\nThe following entity-level permissions are available:\n\n- `auth-policy` - Authentication policy management\n- `ca` - Certificate Authority management\n- `config` - Configuration management\n- `config-type` - Configuration type management\n- `edge-router-policy` - Edge router policy management\n- `enrollment` - Enrollment management\n- `external-jwt-signer` - External JWT signer management\n- `identity` - Identity management\n- `posture-check` - Posture check management\n- `router` - Edge and transit router management\n- `service` - Service management\n- `service-policy` - Service policy management\n- `service-edge-router-policy` - Service edge router policy management\n- `terminator` - Terminator management\n- `ops` - Operational resources (API sessions, sessions, circuits, links, inspect and validate)\n\n### Permission Assignment\n\nPermissions are assigned to identities via the `permissions` field in the identity resource. Multiple permissions can be granted to a single identity, and permissions are additive.\n\n### Cross-Entity Operations\n\nListing related entities through an entity's endpoints requires appropriate permissions for the related entity type. For example:\n- Listing services for a service-policy requires `service.read` permission\n- Listing identities for an edge-router-policy requires `identity.read` permission\n- Listing configs for a service requires `config.read` permission\n\n**NOTE:** \nMore permissions than expected may be required when performing actions through the CLI or ZAC. Take for example, when an identity \nhas `config.create` and is attempting to create a new config. The CLI may fail if the identity doesn't have `config-type.read`\nas well because it will need to look up the config type id that corresponds to the given config type name.\n\nSimilar cross entity read permissions may be required when creating services.\n\n### Admin Protection\n\nNon-admin identities cannot:\n- Create identities with the `isAdmin` flag\n- Create identities with any permissions granted\n- Modify admin-related fields on existing identities\n- Update or delete admin identities\n- Grant permissions to identities\n\nThese protections ensure that privilege escalation is prevented and admin access remains controlled.\n\n\n## Binding Controller APIs With Identity\n\nController APIs can now be bound to an OpenZiti overlay network identity, allowing secure communication through\nthe Ziti network. This is useful for scenarios where you want to expose controller APIs only through the overlay\nnetwork rather than on a standard network interface.\n\n### Configuration Structure\n\nA standard `bindPoint` configuration looks like this:\n```text\n    bindPoints:\n      - interface: 127.0.0.1:18441\n        address: 127.0.0.1:18441\n```\n\nTo bind controller APIs to an OpenZiti identity, add an additional `identity` block to your `bindPoints`. The\nidentity configuration specifies where to load the Ziti identity file and which service to bind it to:\n\n```text\n    bindPoints:\n      - interface: 127.0.0.1:18441\n        address: 127.0.0.1:18441\n      - identity:\n          file: \"c:/temp/ctrl.testing/ctrl.identity.json\"\n          service: \"mgmt\"\n```\n\n### Supported Configuration Options\n\n- `file`: Path to a Ziti identity JSON file containing the controller's identity and enrollment certificate\n- `env`: Name of an environment variable containing a base64-encoded Ziti identity (alternative to `file`)\n- `service`: The name of the Ziti service to bind the controller API to\n\n### Using Environment Variables\n\nFor deployments where storing identity files on disk is not preferred, you can reference a base64-encoded\nidentity file from an environment variable. The environment variable should contain the base64-encoded contents\nof the identity JSON file.\n\nFor example, if an environment variable named `ZITI_CTRL_IDENTITY` contains a base64-encoded identity file:\n\n```text\n    bindPoints:\n      - interface: 127.0.0.1:18441\n        address: 127.0.0.1:18441\n      - identity:\n          env: ZITI_CTRL_IDENTITY\n          service: \"mgmt\"\n```\n\n### IPv6 Support\n\nBoth IPv4 and IPv6 addresses are supported for standard bind points. IPv6 addresses should be specified in bracket\nnotation with a port number:\n\n```text\n    bindPoints:\n      - interface: \"[::1]:18441\"\n        address: \"[::1]:18441\"\n      - identity:\n          file: \"/path/to/identity.json\"\n          service: \"mgmt\"\n```\n\n## CLI Enhancements for Identity-Based Connections\n\nThe `ziti edge login` command and REST client utilities have been enhanced to support identity-based connections\nthrough the Ziti overlay network.\n\n### New `--network-identity` Flag for `ziti edge login`\n\nThe `ziti edge login` command now includes a `--network-identity` flag that allows you to authenticate to a Ziti\ncontroller through the overlay network using a Ziti identity:\n\n```bash\nziti edge login https://ziti.mgmt.apis.local:1280 \\\n  --username myuser \\\n  --password mypass \\\n  --network-identity /path/to/identity.json\n```\n\nThis is useful when the controller is only accessible through the Ziti overlay network or when you want to ensure\nall communication to the controller flows through the overlay for security purposes.\n\n### Identity Resolution Order\n\nWhen establishing connections, identities are resolved in the following order:\n\n1. **Command-line flag**: The `--network-identity` flag takes precedence\n2. **Environment variable**: If `ZITI_CLI_NETWORK_ID` is set and contains a base64-encoded identity, it is used\n3. **Cached identity file**: If a network identity was saved from a previous login in the Ziti config directory, it may be used\n\nThis layered approach allows for flexibility in deployment scenarios:\n- Development: Use command-line flags for quick testing\n- Automation: Use environment variables in CI/CD pipelines\n- Production: Cache identities securely for repeated access\n\n#### Dialing Modes When Authenticating\n\nThe CLI supports two dialing modes:\n\n**Intercept-based Dialing (Default)**\nBy default, URLs are expected to leverage intercepts. Create a service with an appropriate intercept config and use\nthe intercept address when dialing. This is the standard mode for most use cases. For example, given a service with\nthe intercept `ziti.mgmt.apis.local`\n```bash\nziti edge login https://ziti.mgmt.apis.local:1280 \\\n  --username myuser \\\n  --password mypass \\\n  --network-identity /path/to/identity.json\n```\n\n**Identity-aware Dialing (Addressable Terminators)**\nTo support addressable terminators-based dialing, specify a user in the URL. This activates dial-by-identity\nfunctionality. The URL format should be `identity-to-dial@service-name-to-dial`. For example:\n```bash\nziti edge login https://my-identity@my-service:1280 \\\n  --username myuser \\\n  --password mypass \\\n  --network-identity /path/to/identity.json\n```\n\nIn this mode, the transport extracts the identity from the URL and uses it to establish a direct connection to\nthe specified service via the addressable terminator.\n\n\n## OIDC/JWT Token-based Enrollment\n\nOpenZiti now supports provisioning identities just-in-time through OIDC/JWT token enrollment. External identity \nproviders can be configured to allow identities to enroll using JWT tokens, with support for the resulting \nidentities to use certificate or token authentication.\n\n### External JWT Signer Configuration\n\nExternal JWT signers are configured via the Edge Management API to define enrollment behavior with the following new \nenrollment-specific properties:\n\n- **enrollToCertEnabled** - When enabled, identities can exchange a JWT token and a certificate signing request (CSR) \n        for a client certificate during enrollment. The certificate can then be used for standard certificate-based\n        authentication.\n\n- **enrollToTokenEnabled** - When enabled, identities can use a JWT token to enroll. The current token or future tokens\n        may be used for authentication.\n\n- **enrollNameClaimsSelector** - Specifies which JWT claim contains the identity name. Accepts a JSON pointer \n        (e.g., `/preferred_username`) or a simple property name (e.g., `preferred_username`, automatically converted to\n        `/preferred_username`). Defaults to `/sub` if not specified. The extracted value becomes the identity name in Ziti.\n\n- **enrollAttributeClaimsSelector** - Specifies which JWT claims to extract as identity attributes during enrollment.\n        Accepts a JSON pointer (e.g., `/roles`) or a simple property name (e.g., `roles`). Extracted attributes are \n        applied to the newly enrolled identity for use in authorization policies.\n\n- **enrollAuthPolicyId** - Specifies the authentication policy to apply to newly enrolled identities. This determines\n        what authentication methods are available for the identity post-enrollment.\n\nAdditionally the existing property named **claimsProperty** that specifies external id to match identities to:\n\n- now supports a JSON pointer (e.g., `/id`) or a simple property name (e.g., `id`)\n- is used to populate the `externalId` field of the identity\n\n### Enrollment Paths\n\n#### Certificate Enrollment (enrollToCertEnabled)\n\nWhen certificate enrollment is enabled, unauthenticated users can:\n\n1. Obtain a list of available IdPs from the public Edge Client API `GET /external-jwt-signers` endpoint, where \n   `enrollToCertEnabled` is set to `true`\n2. Obtain a JWT from the configured OIDC provider\n3. Generate a certificate signing request (CSR)\n4. Submit an enrollment request with the JWT and CSR\n5. Have their identity created in Ziti with attributes extracted from JWT claims\n6. Receive a signed client certificate for certificate-based authentication\n\n#### Token Enrollment (enrollToTokenEnabled)\n\nWhen token enrollment is enabled, unauthenticated users can:\n\n1. Obtain a list of available IdPs from the public Edge Client API `GET /external-jwt-signers` endpoint, where \n   `enrollToTokenEnabled` is set to `true`\n1. Obtain a JWT from the configured OIDC provider\n2. Submit an enrollment request with the JWT\n3. Have their identity created in Ziti with attributes extracted from JWT claims\n4. Receive a Ziti API token for token-based authentication\n\n### Edge Management API\n\nThe Edge Management API provides full CRUD operations for configuring external JWT signers:\n\n- `POST /external-jwt-signers` - Create a new external JWT signer with all configuration options\n- `GET /external-jwt-signers` - List all configured external JWT signers\n- `GET /external-jwt-signers/{id}` - Retrieve a specific signer configuration\n- `PUT /external-jwt-signers/{id}` - Update all fields of a signer\n- `PATCH /external-jwt-signers/{id}` - Partially update a signer\n- `DELETE /external-jwt-signers/{id}` - Delete a signer\n\n### Edge Client API\n\nThe Edge Client API exposes a reduced set of external JWT signer information for unauthenticated enrollment requests:\n\n- `GET /external-jwt-signers` - List available JWT signers with enrollment capabilities\n\nThe client API response includes the following fields for each signer:\n\n- `name` - Signer name\n- `externalAuthUrl` - URL where users obtain JWT tokens\n- `clientId` - OIDC client ID\n- `scopes` - Requested OIDC scopes\n- `openIdConfigurationUrl` - OIDC discovery endpoint\n- `audience` - Expected token audience\n- `targetToken` - Token type to use (ACCESS or ID)\n- **`enrollToCertEnabled`** - Flag indicating certificate enrollment is available\n- **`enrollToTokenEnabled`** - Flag indicating token enrollment is available\n\n### CLI Commands\n\n**Create an external JWT signer with enrollment options:**\n```\nziti edge controller create ext-jwt-signer <name> <issuer> \\\n  --jwks-endpoint <url> \\\n  --audience <audience> \\\n  --enroll-to-cert \\\n  --enroll-to-token=false \\\n  --enroll-name-claims-selector preferred_username \\\n  --enroll-attr-claims-selector roles \\\n  --enroll-auth-policy <policy-id-or-name>\n```\n\n**Update enrollment options on an existing signer:**\n```\nziti edge controller update ext-jwt-signer <name|id> \\\n  --enroll-to-cert \\\n  --enroll-auth-policy <policy-id-or-name>\n```\n\n**List external JWT signers:**\n```\nziti edge controller list ext-jwt-signers\n```\n\n## Clustering Performance Improvements\n\nIn previous releases, model updates were submitted to raft one at at time. This prevented \nraft from being efficient by allowing command batching. This release allows multiple \nmodel updates to be in-flight at the same time. \n\nNew Configuration Options\n\n1. Raft Apply Timeout (cluster.applyTimeout)\n\nLocation: Controller configuration file, under the cluster section\nType: Duration\nDefault: 5s\nDescription: Timeout for applying commands to the Raft distributed log. Commands that exceed this timeout will trigger adaptive rate limiter backoff.\n\nExample:\n```\ncluster:\n  applyTimeout: 10s\n```\n\n2. Cluster Rate Limiter Configuration (cluster.rateLimiter)\n\nA new adaptive rate limiter that controls the submission of commands to the Raft cluster. Unlike the existing command rate limiter, this specifically manages in-flight Raft operations with adaptive window sizing.\n\nConfiguration Structure:\n```\ncluster:\n  rateLimiter:\n    enabled: true\n    minSize: 5\n    maxSize: 250\n    timeout: 30s\n```\n\nSub-options:\n\n  - enabled (boolean)\n    - Default: true\n    - Description: Enable/disable adaptive rate limiting for Raft command submission\n  - minSize (integer)\n    - Default: 5\n    - Minimum: 1\n    - Description: Minimum window size for concurrent in-flight Raft operations\n  - maxSize (integer)\n    - Default: 250\n    - Description: Maximum window size for concurrent in-flight Raft operations. Must be >= minSize\n  - timeout (duration)\n    - Default: 30s\n    - Description: Time after which outstanding work is assumed to have failed if not marked completed\n\n3. Restart Self on Snapshot (cluster.restartSelfOnSnapshot)\n\nLocation: Controller configuration file, under cluster section\nType: Boolean\nDefault: false\nDescription: When true, the controller will automatically restart itself when restoring a snapshot to an initialized system. When false, the controller will exit with code 0, requiring external process management to restart it.\n\nExample:\n```\ncluster:\n    restartSelfOnSnapshot: true\n```\n\n### New Metrics\n\nThe adaptive rate limiter exposes three new metrics:\n\n  1. raft.rate_limiter.queue_size (gauge)\n    - Current number of operations queued/in-flight\n  2. raft.rate_limiter.work_timer (timer)\n    - Duration of rate-limited operations\n  3. raft.rate_limiter.window_size (gauge)\n    - Current adaptive window size\n\n## Rate Limiter Algorithm Improvements\n\nThe adaptive rate limiter tracker now uses a success rate metric to decide when to grow or shrink its\nconcurrency window, instead of using raw queue position. An exponentially decaying histogram tracks the\nratio of successes to backoffs. When the success rate exceeds a configurable threshold, the window is\ngrown by a multiplicative increase factor. When it falls below the threshold, the window is shrunk by a\nmultiplicative decrease factor. The check intervals for increase and decrease are independently configurable.\n\nThis approach produces smoother, more stable window adjustments under varying load, avoiding the\nover-aggressive shrinking that could occur when queue position alone was used as the signal.\n\nThis specific rate limiter implementation is used in three places:\n* **Controller TLS handshake rate limiting** - controls the rate of incoming TLS handshakes on the controller\n* **Raft command submission** - controls the rate of commands submitted to the Raft distributed log\n* **Router control channel rate limiting** - controls the rate of requests from the router to the controller\n\nNote: this work was done in advance of enabling the TLS handshake rate limiter by default. The TLS\nhandshake rate limiter is not yet enabled by default, but can be enabled via the `tls.rateLimiter`\nconfiguration section.\n\nNew configuration options (available under each `rateLimiter` section):\n\n  - successThreshold (float)\n    - Default: 0.9\n    - Description: Success rate threshold above which the window size will be increased and below which it will be decreased\n  - increaseFactor (float)\n    - Default: 1.02\n    - Description: Multiplier applied to the current window size when growing. Must be greater than 1\n  - decreaseFactor (float)\n    - Default: 0.9\n    - Description: Multiplier applied to the current window size when shrinking. Must be between 0 and 1\n  - increaseCheckInterval (integer)\n    - Default: 10\n    - Description: Number of successes between window size increase checks\n  - decreaseCheckInterval (integer)\n    - Default: 10\n    - Description: Number of backoffs between window size decrease checks\n\n## Background Processing for Identity Updates\n\nIdentity environment and authenticator updates that occur during authentication are now processed asynchronously in the background. \nThis prevents authentication requests from blocking when the system is under load, significantly improving resilience during thundering herd scenarios.\n\nWhen the background queue fills up, updates can be dropped as they will be refreshed on the next authentication attempt. \nThis allows the system to gracefully handle load spikes without impacting authentication performance.\n\nFor now, dropping entries when the queue fills will be disabled by default, but can be enabled, see below.\n\n### Configuration\n\nA new `command.background` configuration section controls the background processing behavior:\n\n```yaml\n  command:\n    background:\n      enabled: true           # Enable background processing (default: true)\n      queueSize: 1000        # Maximum queue size (default: 1000)\n      dropWhenFull: true     # Drop updates when queue is full (default: false)\n      delayThreshold: 50ms   # The threshold for how long updates are taking before starting to background updates\n```\n\nNote that the `commandRateLimiter` configuration section may instead be specified under `command` as `rateLimiter`.\n\nExample:\n\n```yaml\n  command:\n    background:\n      enabled: true\n      queueSize: 250\n      dropWhenFull: false\n      delayThreshold: 50ms\n    rateLimiter:\n      enabled:   true\n      maxQueued: 25\n```\n\nNote that if command rate limiter configuration is specified in both locations, the settings under `command` will take \nprecedence. The standalone `commandRateLimiter` section may be deprecated in the future.\n\n### Metrics\n\nWhen background processing is enabled, the following metrics are exposed:\n\n- command.background.queue_size - Current number of queued background tasks\n- command.background.worker_count - Current number of worker goroutines\n- command.background.busy_workers - Number of workers currently processing tasks\n- command.background.work_timer - Timer tracking background task execution (includes histogram, meter, and count)\n- command.background.dropped_entries - Count of dropped updates when queue is full (only when dropWhenFull is enabled)\n\n## New proxy.v1 Config Type\n\n*Originally released in 1.7.0*\n\nAdded support for dynamic service proxies with configurable binding and protocol options.\nThis allows Edge Routers and Tunnelers to create proxy endpoints that can forward traffic for Ziti services.\n\nThis differs from intercept.v1 in that intercept.v1 will intercept traffic on specified\nIP addresses or DNS entries to forward to a service using tproxy or tun interface,\ndepending on implementation.\n\nA proxy on the other hand will just start a regular TCP/UDP listener on the configured port,\nso traffic will have to be configured for that destination.\n\nExample proxy.v1 Configuration:\n\n```\n  {\n    \"port\": 8080,\n    \"protocols\": [\"tcp\"],\n    \"binding\": \"0.0.0.0\"\n  }\n```\n\nConfiguration Properties:\n  - port (required): Port number to listen on (1-65535)\n  - protocols (required): Array of supported protocols (tcp, udp)\n  - binding (optional): Interface to bind to. For the ER/T defaults to the configured lanIF config property.\n\nThis config type is currently supported by the ER/T when running in either proxy or tproxy mode.\n\n## Alert Events (BETA)\n\n*Originally released in 1.7.0*\n\nA new alert event type has been added to allow Ziti components to emit alerts for issues that network operators can address.\nAlert events are generated when components encounter problems such as service configuration errors or resource\navailability issues.\n\nAlert events include:\n  - Alert source type and ID (currently supports routers, with controller and SDK support planned for future releases)\n  - Severity level (currently supports error, with info and warning planned for future releases)\n  - Alert message and supporting details\n  - Related entities (router, identity, service, etc.) associated with the alert\n\nExample alert event when a router cannot bind a configured network interface:\n\n```\n  {\n    \"namespace\": \"alert\",\n    \"event_src_id\": \"ctrl1\",\n    \"timestamp\": \"2021-11-08T14:45:45.785561479-05:00\",\n    \"alert_source_type\": \"router\",\n    \"alert_source_id\": \"DJFljCCoLs\",\n    \"severity\": \"error\",\n    \"message\": \"error starting proxy listener for service 'test'\",\n    \"details\": [\n      \"unable to bind eth0, no address\"\n    ],\n    \"related_entities\": {\n      \"router\": \"DJFljCCoLs\",\n      \"identity\": \"DJFljCCoLs\",\n      \"service\": \"3DPjxybDvXlo878CB0X2Zs\"\n    }\n  }\n```\n\nAlert events can be consumed through the standard event system and logged to configured event handlers for monitoring and alerting purposes.\n\nThese events are currently in Beta, as the format is still subject to change. Once they've been in use in production for a while\nand proven useful, they will be marked as stable.\n\n## Azure Service Bus Event Sink\n\n*Originally released in 1.7.0. Contributed by @ffaraone.*\n\nAdds support for streaming controller events to Azure Service Bus.\nThe new logger enables real-time event streaming from the OpenZiti controller to Azure Service Bus\nqueues or topics, providing integration with Azure-based monitoring and analytics systems.\n\nTo enable the Azure Service Bus event logger, add configuration to the controller config file under the events section:\n\n```\n  events:\n    serviceBusLogger:\n      subscriptions:\n        - type: circuit\n        - type: session\n        - type: metrics\n          sourceFilter: .*\n          metricFilter: .*\n        # Add other event types as needed\n      handler:\n        type: servicebus\n        format: json\n        connectionString: \"Endpoint=sb://your-namespace.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=your-key\"\n        topic: \"ziti-events\"          # Use 'topic' for Service Bus topic\n        # queue: \"ziti-events-queue\"  # Or use 'queue' for Service Bus queue\n        bufferSize: 100                # Optional, defaults to 50\n```\n\n- Required configuration:\n    - format: Event format, currently supports only json\n    - connectionString: Azure Service Bus connection string\n    - Either topic or queue: Destination name (mutually exclusive)\n\n- Optional configuration:\n    - bufferSize: Internal message buffer size (default: 50)\n\n## OIDC is now enabled by default\n\nThe controller now automatically adds the `edge-oidc` API binding to any web listener that hosts\nthe `edge-client` API, even when `edge-oidc` is not explicitly listed in the `web` section of the\nconfiguration. This means OIDC-based authentication is available out of the box without requiring\nchanges to existing controller configurations.\n\n### Where OIDC binds\n\nThe `edge-oidc` binding is added to the same web listener(s) as `edge-client`. If `edge-client` is\nhosted on `127.0.0.1:1280`, the OIDC endpoints will be available on that same address under the\n`/oidc` path (e.g. `https://127.0.0.1:1280/oidc/.well-known/openid-configuration`).\n\nThe controller capabilities returned by `GET /version` will include `OIDC_AUTH` and the\n`edge-oidc` entry will be present in `apiVersions` when OIDC is active.\n\n### Opting out\n\nIf OIDC should not be enabled automatically, set `disableOidcAutoBinding: true` under `edge.api`:\n\n```yaml\nedge:\n  api:\n    address: 127.0.0.1:1280\n    sessionTimeout: 30m\n    disableOidcAutoBinding: true\n```\n\nWhen `disableOidcAutoBinding` is `true`, OIDC will only be active if `edge-oidc` is explicitly\nlisted as a binding in the `web` section. \n\nWhen OIDC is not enabled, all (SDK) clients will revert to using legacy authentication.\nLegacy authentication does not support multiple controllers or HA in general. Clients will treat\ntheir controller as if it is a single controller instance and will function as if no other controllers\nexist.\n\n\n## WWW-Authenticate Headers\n\nThe controller's Edge APIs now include `WWW-Authenticate` headers on `401 Unauthorized` responses,\nper issuer: https://github.com/openziti/ziti/issues/3356. These headers provide insight into why\na token was rejected. The main benefit of these headers is to convey information for JWT backed\nAPI Sessions and API Session authentication where a token may not have been provided (missing),\nis used beyond its expiration date (expired), or the token has become invalid for any other\nreason (invalid).\n\n`www-authenticate` headers are provided as a single header instance with multiple challenge values\nseparate by commas.\n\n### No Credentials Provided\n\nWhen a request hits a protected endpoint without any credentials, a single `WWW-Authenticate` header\nis returned listing both accepted auth schemes as comma-separated challenges:\n\n```\nWWW-Authenticate: zt-session realm=\"zt-session\" error=\"missing\" error_description=\"no matching token was provided\",Bearer realm=\"openziti-oidc\" error=\"missing\" error_description=\"no matching token was provided\"\n```\n\n### Token Errors\n\nWhen a token is present but cannot be accepted, the header identifies the scheme and what went wrong:\n\n```\nWWW-Authenticate: Bearer realm=\"openziti-oidc\" error=\"expired\" error_description=\"token expired\"\nWWW-Authenticate: Bearer realm=\"openziti-oidc\" error=\"invalid\" error_description=\"token is invalid\"\nWWW-Authenticate: zt-session realm=\"zt-session\" error=\"invalid\" error_description=\"token is invalid\"\n```\n\n### OIDC External JWT — Primary Authentication\n\nWhen an auth policy requires an external JWT signer for primary authentication (e.g., a PKCE flow\nbacked by an ext-jwt signer), the header identifies which signers are accepted and what went wrong.\nMultiple accepted signers are pipe-delimited in the `id` and `issuer` parameters:\n\n```\nWWW-Authenticate: Bearer realm=\"openziti-primary-ext-jwt\" error=\"missing\" error_description=\"no matching token was provided\" id=\"signer-id-1|signer-id-2\" issuer=\"https://issuer1.example.com|https://issuer2.example.com\"\n```\n\nThe `error` value follows the same `missing`/`expired`/`invalid` pattern as standard bearer token errors.\n\n### OIDC External JWT — Secondary / MFA Authentication\n\nWhen an auth policy requires an external JWT signer as a secondary factor (step-up after primary\nauth succeeds), the header identifies the single required signer. The `error` value follows the\nsame `missing`/`expired`/`invalid` pattern:\n\n```\nWWW-Authenticate: Bearer realm=\"openziti-secondary-ext-jwt\" error=\"missing\" error_description=\"no matching token was provided\" id=\"<signer-id>\" issuer=\"<issuer>\"\n```\n\n### Anonymous Endpoints\n\nUnauthenticated endpoints such as version information do not return `WWW-Authenticate` headers.\n\n## HA Preferred Leaders\n\nControllers can be marked as a preferred leader. \n\n**Example Config**\n```yaml\ncluster:\n  dataDir: /home/{{ .Model.MustVariable \"credentials.ssh.username\" }}/fablab/ctrldata\n  preferredLeader: true\n```\n\nIf a controller that is not marked preferredLeader becomes a preferredLeader, it will check \nif there's a node available that is marked as preferred. If there is one, or one later\njoins the cluster, the non-preferred node will attempt to transfer leadership to the \nnode that is marked as preferred.\n\n## Expanded Dynamic Cost Range\n\nThe dynamic cost range for smart routing has been expanded beyond the previous 64K limit. Under high\nload, terminators could saturate the cost space, making dynamic cost values meaningless for routing\ndecisions and leading to uneven load distribution. The expanded range allows for more granular cost\ndifferentiation even under heavy load.\n\n## Circuit ID and Error in Dial Failures\n\nDial failures now return the circuit ID and, when available, the error that caused the circuit to fail.\nPreviously, the circuit ID was only returned on successful dials. Note that SDKs will need to be\nupdated to surface the circuit id when a dial failure happens.\n\n## Multi-Underlay Control Channels\n\nRouter-to-controller control channels now support multiple underlays with priority-based message routing.\nThis allows time-sensitive control messages (heartbeats, routing, circuit requests) to be separated from\noperational data (metrics, inspections) across dedicated TCP connections, preventing bulk operations from\ndelaying user-affecting control plane traffic. This feature does not yet allow specifying multiple \nnetwork interfaces to use, to load balance data across.\n\n## Dialing Identity Forwarded to Hosting SDK\n\nThe identity ID and name of the dialing client are now forwarded to the hosting SDK when a circuit is\nestablished. This allows hosting applications to identify which identity initiated the connection,\nenabling identity-aware request handling on the server side. This will require SDK updates to add this\nto the API for hosting applications.\n\n## Controller-Initiated Control Channel Dials (BETA)\n\nControllers can now dial routers to establish control channels. Previously, routers were solely\nresponsible for dialing controllers. This feature is designed for deployments where one or more\ncontrollers are in a private network that routers cannot reach, but the controllers can dial out.\nA common scenario is an HA cluster where some controllers are publicly reachable and some are in\na private network. External routers connect to the public controllers normally, and the private\ncontrollers dial out to the routers.\n\n### Router Configuration\n\nRouters can configure one or more control channel listeners. Each listener specifies a bind address,\nan advertise address (reported to the controller), and optional groups for matching.\n\n```yaml\nctrl:\n  listeners:\n    - bind: tls://0.0.0.0:6262\n      advertise: tls://router.example.com:6262\n      groups:\n        - default\n```\n\nThe router is the authoritative source of ctrl channel listener information, similar to link\nlisteners. When a router connects to any controller, it reports its configured `ctrlChanListeners`\nand the controller data model is updated automatically. This means that in most deployments —\nwhere the router can reach at least one controller — no manual configuration of listener addresses\nis needed on the controller side.\n\nThe `ctrlChanListeners` field can also be set via the CLI for cases where a router cannot reach\nany controller and thus cannot initialize the data model itself:\n\n```bash\nziti edge update edge-router myRouter --bootstrap-ctrl-chan-listener 'tls://router.example.com:6262=group1,group2'\n```\n\nGroups default to `[\"default\"]` if not specified.\n\n### Controller Configuration\n\nThe controller dialer is disabled by default and must be explicitly enabled. When enabled, the\ncontroller will dial routers that have control channel listeners configured and are not already\nconnected.\n\n```yaml\nctrl:\n  dialer:\n    enabled: true\n    groups:\n      - default\n    dialDelay: 30s\n    minRetryInterval: 1s\n    maxRetryInterval: 5m\n    retryBackoffFactor: 1.5\n    fastFailureWindow: 5s\n    queueSize: 32\n    maxWorkers: 10\n```\n\n- `enabled` - Enables the controller dialer (default: `false`)\n- `groups` - List of groups to match against router listener groups (default: `[\"default\"]`)\n- `dialDelay` - Delay before the controller starts dialing after boot (default: `30s`)\n- `minRetryInterval` - Minimum backoff delay between dial retries (default: `1s`)\n- `maxRetryInterval` - Maximum backoff delay between dial retries (default: `5m`)\n- `retryBackoffFactor` - Multiplier applied to the retry delay on each failure, jittered +/- 0.5 (default: `1.5`)\n- `fastFailureWindow` - If a connection drops within this window after connecting, backoff continues rather than resetting (default: `5s`)\n- `queueSize` - Maximum number of pending dial jobs in the worker pool queue (default: `32`)\n- `maxWorkers` - Maximum number of concurrent dial workers (default: `10`)\n\nThe controller will only dial routers whose listener groups overlap with the controller's configured\ngroups. When a router advertises multiple ctrl channel listener addresses, the dialer rotates through\nthem on each failure so that an unreachable address does not block attempts to the others.\n\n### Metrics\n\nThe controller dialer worker pool exposes the following metrics under the `ctrl_channel.dialer` prefix:\n\n- `ctrl_channel.dialer.queue_size` - Current number of pending dial jobs in the queue\n- `ctrl_channel.dialer.worker_count` - Current number of dial worker goroutines\n- `ctrl_channel.dialer.busy_workers` - Number of workers currently executing a dial\n- `ctrl_channel.dialer.work_timer` - Timer tracking the duration of each dial attempt\n\n## SDK Inspection Support\n\nNew CLI commands have been added for inspecting SDK state, useful for diagnosing terminator and\nconnectivity issues.\n\n**Note:** SDK inspection requires SDK support. Currently only the Go SDK supports inspection,\nas of version 1.5.0. Other SDKs will need to add support before these commands can be used\nwith them.\n\n### `ziti fabric inspect sdk`\n\nRetrieves SDK context inspection data from identities connected to routers.\n\n```\nziti fabric inspect sdk <target-selector> <identity-id>\n```\n\nThe `<target-selector>` is a regex matching router IDs (use `.*` for all routers). The\n`<identity-id>` is the identity whose SDK context you want to inspect. The command returns\ndetailed state from the connected SDK instance, including active services, terminators, and\nconnection status.\n\n### `ziti agent tunnel dump-sdk`\n\nDumps SDK context information from a running `ziti tunnel` process via the IPC agent.\n\n```\nziti agent tunnel dump-sdk\n```\n\nReturns JSON-formatted inspection data for all SDK contexts registered in the tunnel process,\nincluding service listeners, connections, and terminator state.\n\n## Revocation System Improvements\n\nWhen a session is refreshed, the old refresh token's revocation is no longer created\nsynchronously through raft. Instead, revocations are queued in memory and flushed in\nbatches on a configurable interval. This removes the database and raft as a bottleneck\non token refreshes. If the old token is close to expiring, the revocation is skipped\nentirely.\n\nNew configuration tunables under `edge.oidc`:\n\n| Key | Default | Description |\n|-----|---------|-------------|\n| `revocationMinTokenLifetime` | unset | Skip revocation if the old token expires within this duration (must be < 50% of `refreshTokenDuration`) |\n| `revocationBucketInterval` | `1m` | Bucket window for batching revocations before flushing through raft |\n| `revocationBucketMaxSize` | `200` | Max revocations per raft entry |\n| `revocationMaxQueued` | `25000` | Max revocations queued in memory before dropping |\n| `revocationEnforcerFrequency` | `1m` | How often expired revocations are purged (leader only) |\n\n## CLI Reorganization Details\n\nThe CLI has been reorganized so that edge and fabric entity management commands are \navailable directly at the top level. The `ziti edge` and `ziti fabric` command trees \nremain fully functional — the new top-level commands are additional entry points, not \nreplacements.\n\n### Top-Level CRUD Commands\n\nEntity create, delete, list, and update operations that previously required the \n`ziti edge` or `ziti fabric` prefix are now available directly:\n\n| New command | Previous command |\n|---|---|\n| `ziti create identity ...` | `ziti edge create identity ...` |\n| `ziti delete service ...` | `ziti edge delete service ...` |\n| `ziti list edge-routers` | `ziti edge list edge-routers` |\n| `ziti update identity ...` | `ziti edge update identity ...` |\n| `ziti list circuits` | `ziti fabric list circuits` |\n| `ziti delete terminator ...` | `ziti fabric delete terminator ...` |\n\nAll edge and fabric entities are available under the consolidated commands. When an \nentity name exists in both edge and fabric (e.g., `service`, `router`), the edge \nversion is the default. Fabric equivalents are accessible with a `fabric-` prefix \n(e.g., `ziti list fabric-services`).\n\n### Top-Level Login\n\nSession management is now available at the top level:\n\n| New command | Previous command |\n|---|---|\n| `ziti login` | `ziti edge login` |\n| `ziti login forget` | `ziti edge login forget` |\n| `ziti login use` | `ziti edge use` |\n\n### Breaking Change: `ziti create ca`\n\n`ziti create ca` now creates a Ziti edge Certificate Authority, matching the \nbehavior of `ziti edge create ca`. Previously, `ziti create ca` was a PKI command \nfor generating CA certificates on the local filesystem.\n\nThe PKI command is still available at its original location:\n\n```\nziti pki create ca ...\n```\n\nScripts that use `ziti create ca` for PKI operations should be updated to use \n`ziti pki create ca` instead.\n\n## Multiple DNS Upstreams\n\nThe tunneler's DNS resolver now accepts multiple upstream DNS servers and fans out recursive queries\nto all of them in parallel, rather than being limited to a single upstream. This is useful for split-horizon\nsetups where different resolvers are authoritative for different zones, and for environments where a primary\nresolver may be slow or unreachable.\n\n### CLI\n\nThe `ziti tunnel --dnsUpstream` flag is now a repeatable string slice. Upstreams can be listed by repeating\nthe flag or by passing a comma-separated value:\n\n```bash\nziti tunnel run \\\n  --dnsUpstream udp://10.96.0.10:53 \\\n  --dnsUpstream tcp://8.8.8.8:53\n```\n\n### Router Config\n\nIn `xgress_edge_tunnel` router configs, `options.dnsUpstream` now accepts either a single string (as before)\nor a list of strings. Existing configs continue to work unchanged:\n\n```yaml\n# single upstream (unchanged)\noptions:\n  dnsUpstream: udp://10.96.0.10:53\n\n# multiple upstreams\noptions:\n  dnsUpstream:\n    - udp://10.96.0.10:53\n    - tcp://8.8.8.8:53\n```\n\n### How Resolution Works\n\nWhen a query comes in, the resolver dispatches it to every configured upstream concurrently. The first\nresponse with `RCODE=NOERROR` wins and is returned to the client immediately, so split-horizon lookups\nwork even if one upstream is slow. If no upstream returns NOERROR, the resolver picks the best-ranked\nnon-NOERROR reply (NXDOMAIN > SERVFAIL > REFUSED) so authoritative negative answers aren't masked by\ntransport failures. If every upstream fails to respond, the query is treated as unanswerable and handled\nper the configured `dnsUnanswerable` disposition.\n\n## Agent Inspect\n\nThe `ziti agent` CLI now has a generic `inspect` subcommand that works against any ziti process\n(controller, router, or tunneler) over the local IPC agent channel. It sends the inspect request\ndirectly to the target process, so unlike `ziti fabric inspect` it doesn't fan out through the\ncontroller and doesn't require network connectivity to the target.\n\n```\nziti agent inspect <value> [values...]\n```\n\nValues are matched against whatever the target process exposes. Common inspect keys:\n\n* Routers: `stackdump`, `links`, `config`, `metrics`, `sdk-terminators`, `ert-terminators`,\n  `router-circuits`, `router-data-model`, `router-controllers`\n* Controllers: `stackdump`, `config`, `metrics`, `connected-routers`, `connected-peers`,\n  `cluster-config`, `router-messaging`, `terminator-costs`, `data-model-index`\n* Tunnelers: `stackdump`, `sdk`\n\n## Current Beta Features\n\nBeta features are still under development and are subject to change. They should\nbe usable in their released form. Though unlikely, there is a small chance they will \nbe removed. \n\n* Basic Permission System\n* Alert Events\n* Controller-Initiated Control Channel Dials\n\n## Component Updates and Bug Fixes\n\n* github.com/openziti/agent: [v1.0.31 -> v1.0.33](https://github.com/openziti/agent/compare/v1.0.31...v1.0.33)\n* github.com/openziti/channel/v4: [v4.2.28 -> v4.3.11](https://github.com/openziti/channel/compare/v4.2.28...v4.3.11)\n    * [Issue #242](https://github.com/openziti/channel/issues/242) - Reconnecting channel shouldn't allow changing ids\n    * [Issue #235](https://github.com/openziti/channel/issues/235) - Bump allowed hello message headers size to 16k from 4k\n    * [Issue #228](https://github.com/openziti/channel/issues/228) - Ensure that Underlay never return nil on MultiChannel\n    * [Issue #226](https://github.com/openziti/channel/issues/226) - Allow specifying a minimum number of underlays for a channel, regardless of underlay type\n    * [Issue #225](https://github.com/openziti/channel/issues/225) - Add ChannelCreated to the UnderlayHandler API to allow handlers to be initialized with the channel before binding\n    * [Issue #224](https://github.com/openziti/channel/issues/224) - Update the underlay dispatcher to allow unknown underlay types to fall through to the default\n    * [Issue #222](https://github.com/openziti/channel/issues/222) - Allow injecting the underlay type into messages\n\n* github.com/openziti/edge-api: [v0.26.47 -> v0.28.1](https://github.com/openziti/edge-api/compare/v0.26.47...v0.28.1)\n    * [Issue #175](https://github.com/openziti/edge-api/issues/175) - ctrlChanListeners should have x-omit-empty: false attribute\n    * [Issue #170](https://github.com/openziti/edge-api/issues/170) - Add preferredLeader flag to controllers\n    * [Issue #167](https://github.com/openziti/edge-api/issues/167) - Add ctrlChanListeners to router types\n    * [Issue #164](https://github.com/openziti/edge-api/issues/164) - Add permissions list to identity\n\n* github.com/openziti/foundation/v2: [v2.0.72 -> v2.0.91](https://github.com/openziti/foundation/compare/v2.0.72...v2.0.91)\n    * [Issue #472](https://github.com/openziti/foundation/issues/472) - Add support for multi-bit set/get to AtomicBitSet\n    * [Issue #464](https://github.com/openziti/foundation/issues/464) - Add support for -pre in versions\n    * [Issue #455](https://github.com/openziti/foundation/issues/455) - Correctly close goroutine pool when external close is signaled\n    * [Issue #452](https://github.com/openziti/foundation/issues/452) - Goroutine pool with a min worker count of 1 can drop to 0 workers due to race condition\n\n* github.com/openziti/identity: [v1.0.111 -> v1.0.129](https://github.com/openziti/identity/compare/v1.0.111...v1.0.129)\n    * [Issue #68](https://github.com/openziti/identity/issues/68) - Shutdown file watcher when stopping identity watcher\n\n* github.com/openziti/metrics: [v1.4.2 -> v1.4.5](https://github.com/openziti/metrics/compare/v1.4.2...v1.4.5)\n    * [Issue #58](https://github.com/openziti/metrics/issues/58) - Add GaugeFloat64 support\n    * [Issue #56](https://github.com/openziti/metrics/issues/56) - underlying resources of reference counted meters are not cleaned up when reference count hits zero\n\n* github.com/openziti/runzmd: [v1.0.80 -> v1.0.90](https://github.com/openziti/runzmd/compare/v1.0.80...v1.0.90)\n* github.com/openziti/sdk-golang: [v1.2.3 -> v1.7.0](https://github.com/openziti/sdk-golang/compare/v1.2.3...v1.7.0)\n    * [Issue #901](https://github.com/openziti/sdk-golang/issues/901) - Move xgress back to having retransmitter goroutine per-xgress\n    * [Issue #906](https://github.com/openziti/sdk-golang/issues/906) - Fix potential nil references on session service structs\n    * [Issue #897](https://github.com/openziti/sdk-golang/issues/897) - Allow xgress to use pull model for reads when appropriate\n    * [Issue #895](https://github.com/openziti/sdk-golang/issues/895) - Limit effect sudden rtt spikes can have on rtt moving average\n    * [Issue #902](https://github.com/openziti/sdk-golang/issues/902) - Inspect response message content types are mixed up\n    * [Issue #887](https://github.com/openziti/sdk-golang/issues/887) - Fix listener manager cleanup\n    * [Issue #886](https://github.com/openziti/sdk-golang/issues/886) - When controller is busy during service refresh, backoff and retry instead of falling back to full refresh\n    * [Issue #885](https://github.com/openziti/sdk-golang/issues/885) - Only compare relevant service fields when looking for changes\n    * [Issue #884](https://github.com/openziti/sdk-golang/issues/884) - Add deadline for bind establishment\n    * [Issue #883](https://github.com/openziti/sdk-golang/issues/883) - Router level listener can be left open if multi-listener closes during listener establishment\n    * [Issue #877](https://github.com/openziti/sdk-golang/issues/877) - Handle differences in xgress eof/end-of-circuit handling by adding a capabilities exchange\n    * [Issue #832](https://github.com/openziti/sdk-golang/issues/832) - Fuzz session refresh timers\n    * [Issue #879](https://github.com/openziti/sdk-golang/issues/879) - Return the connId in inspect response\n    * [Issue #878](https://github.com/openziti/sdk-golang/issues/878) - Fix responses from rx goroutines\n    * [Issue #874](https://github.com/openziti/sdk-golang/issues/874) - Add inspect support at the context level\n    * [Issue #871](https://github.com/openziti/sdk-golang/issues/871) - Make SDK better at sticking to MaxTerminator terminators\n    * [Issue #708](https://github.com/openziti/sdk-golang/issues/708) - Support for Go's built-in context in Dial methods\n    * [Issue #860](https://github.com/openziti/sdk-golang/issues/860) - Make the dialing identity's id and name available on dialed connections\n    * [Issue #857](https://github.com/openziti/sdk-golang/issues/857) - Use new error code and retry hints to correctly react to terminator errors\n    * [Issue #847](https://github.com/openziti/sdk-golang/issues/847) - Ensure the initial version check succeeds, to ensure we don't legacy sessions on ha or oidc-enabled controllers\n    * [Issue #824](https://github.com/openziti/sdk-golang/pull/824) - release notes and hard errors on no TOTP handler breaks partial auth events\n    * [Issue #818](https://github.com/openziti/sdk-golang/issues/818) - Full re-auth should not clear services list, as that breaks the on-change logic\n    * [Issue #817](https://github.com/openziti/sdk-golang/issues/817) - goroutines can get stuck when iterating over randomized HA controller list\n    * [Issue #736](https://github.com/openziti/sdk-golang/issues/736) - Migrate from github.com/mailru/easyjson\n    * [Issue #813](https://github.com/openziti/sdk-golang/issues/813) - SDK doesn't stop close listener when it detects that a service being hosted gets deleted\n    * [Issue #811](https://github.com/openziti/sdk-golang/issues/811) - Credentials are lost when explicitly set\n    * [Issue #807](https://github.com/openziti/sdk-golang/issues/807) - Don't send close from rxer to avoid blocking\n    * [Issue #800](https://github.com/openziti/sdk-golang/issues/800) - Tidy create service session logging\n\n* github.com/openziti/secretstream: [v0.1.39 -> v0.1.49](https://github.com/openziti/secretstream/compare/v0.1.39...v0.1.49)\n* github.com/openziti/transport/v2: [v2.0.188 -> v2.0.215](https://github.com/openziti/transport/compare/v2.0.188...v2.0.215)\n    * [Issue #31](https://github.com/openziti/transport/issues/31) - ipv6 Transport Address Parsing\n    * [Issue #149](https://github.com/openziti/transport/issues/149) - Archive transwarp code\n\n* github.com/openziti/xweb/v3: [v2.3.4 -> v3.0.4](https://github.com/openziti/xweb/compare/v2.3.4...v3.0.4)\n    * [Issue #32](https://github.com/openziti/xweb/issues/32) - watched identities sometimes don't reload when changed\n\n* github.com/openziti/go-term-markdown: v1.0.1 (new)\n* github.com/openziti/ziti/v2: [v1.6.8 -> v2.0.0](https://github.com/openziti/ziti/compare/v1.6.8...v2.0.0)\n    * [Issue #3860](https://github.com/openziti/ziti/issues/3860) - Router data model index resets to 0 on controller restart\n    * [Issue #3857](https://github.com/openziti/ziti/issues/3857) - Router first-party cert check should include intermediates from the TLS peer chain\n    * [Issue #3855](https://github.com/openziti/ziti/issues/3855) - Filter current api session certs list by current api session\n    * [Issue #3838](https://github.com/openziti/ziti/issues/3838) - List controllers on management API has incorrect permissions check\n    * [Issue #3837](https://github.com/openziti/ziti/issues/3837) - Create db snapshot with path has incorrect permissions check\n    * [Issue #3846](https://github.com/openziti/ziti/issues/3846) - OIDC token binds client cert during non-cert auth, causes PoP failures\n    * [Issue #3809](https://github.com/openziti/ziti/issues/3809) - Support CSR submission during OIDC authentication for session-bound certificates\n    * [Issue #3830](https://github.com/openziti/ziti/issues/3830) - statemanager is holding on to edge connections and they're never getting cleared\n    * [Issue #3824](https://github.com/openziti/ziti/issues/3824) - Allow calling inspect using the IPC agent on the controller, router and go tunnel\n    * [Issue #2049](https://github.com/openziti/ziti/issues/2049) - The ziti agent command should have a controller connection status\n    * [Issue #3784](https://github.com/openziti/ziti/issues/3784) - Fix link registry race condition on reporting links on reconnect\n    * [Issue #3734](https://github.com/openziti/ziti/issues/3734) - Enforce client certificate proof-of-possession on controller REST API for OIDC sessions\n    * [Issue #3806](https://github.com/openziti/ziti/issues/3806) - Expose OpenZiti-specific login and MFA endpoints in the OIDC discovery document\n    * [Issue #3818](https://github.com/openziti/ziti/issues/3818) - Filtering policies by keywords `Dial` and `Bind` doesn't work\n    * [Issue #3816](https://github.com/openziti/ziti/issues/3816) - Support multiple upstream DNS providers in ziti tunnel and ER/T\n    * [Issue #3699](https://github.com/openziti/ziti/issues/3699) - Consolidate CLI edge and fabric commands in top level create/update/delete/list/login commands\n    * [Issue #3788](https://github.com/openziti/ziti/issues/3788) - OIDC Endpoints return 400 Bad Request instead of underlying error\n    * [Issue #3680](https://github.com/openziti/ziti/issues/3680) - adds revocation control to CLI and Management API\n    * [Issue #3717](https://github.com/openziti/ziti/issues/3717) - Generic error message for specific error\n    * [Issue #3543](https://github.com/openziti/ziti/issues/3543) - New Circuit Failure code for sockets not available\n    * [Issue #3364](https://github.com/openziti/ziti/issues/3364) - Make no such host error specific\n    * [Issue #2888](https://github.com/openziti/ziti/issues/2888) - New specific Error code for port not allowed\n    * [Issue #2859](https://github.com/openziti/ziti/issues/2859) - Create specific error code for DNS failed resolution\n    * [Issue #1580](https://github.com/openziti/ziti/issues/1580) - Invalid link destination should have a specific error code\n    * [Issue #3706](https://github.com/openziti/ziti/issues/3706) - Increase link payload/ack queue sizes and make them configurable\n    * [Issue #3778](https://github.com/openziti/ziti/issues/3778) - SetRouterDataModel can deadlock in the router\n    * [Issue #3777](https://github.com/openziti/ziti/issues/3777) - With the new circuit reserve, we can have circuits with no path in the controller circuit set, which can cause panics\n    * [Issue #3770](https://github.com/openziti/ziti/issues/3770) - Update Token Requests Should Close Channel Connections If Invalid\n    * [Issue #3762](https://github.com/openziti/ziti/issues/3762) - Revocations not included in full router data model state\n    * [Issue #3757](https://github.com/openziti/ziti/issues/3757) - Mesh peer signing cert from header is overwritten by TLS underlay cert\n    * [Issue #3756](https://github.com/openziti/ziti/issues/3756) - TLS handshake rate limiter timeout check reads from wrong config scope\n    * [Issue #3755](https://github.com/openziti/ziti/issues/3755) - commandHandler config read from wrong scope\n    * [Issue #3754](https://github.com/openziti/ziti/issues/3754) - dialFailed drops applyFailed parameter, preventing duplicate link retry jitter\n    * [Issue #3753](https://github.com/openziti/ziti/issues/3753) - SPIFFE trust domain prefix check has swapped HasPrefix arguments\n    * [Issue #3746](https://github.com/openziti/ziti/issues/3746) - The controller connect events control channel handler leaks a goroutine\n    * [Issue #3747](https://github.com/openziti/ziti/issues/3747) - Update controller peer error marshalling for app code changes\n    * [Issue #3721](https://github.com/openziti/ziti/issues/3721) - Add CreateCircuitV3 to controller\n    * [Issue #3719](https://github.com/openziti/ziti/issues/3719) - Allow binding specific inspects to pass through to xgress listener implementations\n    * [Issue #3696](https://github.com/openziti/ziti/issues/3696) - oidc provider is non-deterministic for wildcard certs\n    * [Issue #3681](https://github.com/openziti/ziti/issues/3681) - coalesce OIDC JWT revocations to reduce controller write pressure\n    * [Issue #3683](https://github.com/openziti/ziti/issues/3683) - add fablab test for testing flow control changes over a longer term\n    * [Issue #3673](https://github.com/openziti/ziti/issues/3673) - revocation build-up in db and rdm\n    * [Issue #3674](https://github.com/openziti/ziti/issues/3674) - Update to Go 1.26\n    * [Issue #3496](https://github.com/openziti/ziti/issues/3496) - MFA TOTP Enrollment During OIDC Authentication Does Not Work\n    * [Issue #3609](https://github.com/openziti/ziti/issues/3609) - Stabilize terminator creation test for 2.0\n    * [Issue #3648](https://github.com/openziti/ziti/issues/3648) - tunnel: myCopy logs router ID as circuitId, causing misleading debug output\n    * [Issue #3658](https://github.com/openziti/ziti/issues/3658) - Raft cluster join fails with \"hello message too big\" when using long hostnames\n    * [Issue #3626](https://github.com/openziti/ziti/issues/3626) - controller: overlay bind point produces malformed URL in /versions apiBaseUrls\n    * [Issue #3635](https://github.com/openziti/ziti/issues/3635) - Allow controllers to dial routers to support more topologies\n    * [Issue #3607](https://github.com/openziti/ziti/issues/3607) - linux installer not upgradable from v1\n    * [Issue #3650](https://github.com/openziti/ziti/issues/3650) - Reroute doesn't proactively clean up orphaned route entries\n    * [Issue #3571](https://github.com/openziti/ziti/issues/3571) - Ensure 2.0 backwards compatibility with 1.6 and 1.5 using the smoketest\n    * [Issue #3636](https://github.com/openziti/ziti/issues/3636) - Adaptive rate limiter should use success rate rather than queue position\n    * [Issue #3600](https://github.com/openziti/ziti/issues/3600) - Add a preferredLeader flag, allow selected nodes to be preferred for raft leader, if they're available\n    * [Issue #3642](https://github.com/openziti/ziti/issues/3642) - Use xgress_common.Connection type for xgress_transport and xgress_proxy\n    * [Issue #3597](https://github.com/openziti/ziti/issues/3597) - Enable OIDC API by default\n    * [Issue #3624](https://github.com/openziti/ziti/issues/3624) - Multi-underlay control channel doesn't correctly handle lack of group secret on non-grouped underlays\n    * [Issue #3613](https://github.com/openziti/ziti/issues/3613) - Initializing cluster from existing db using `db:` config settings results in panic\n    * [Issue #3620](https://github.com/openziti/ziti/issues/3620) - Controller control channel heartbeats config parsing was erroneously nested under options parsing\n    * [Issue #3619](https://github.com/openziti/ziti/issues/3619) - Router connect events full sync interval was using min/max values meant for the batch interval\n    * [Issue #3618](https://github.com/openziti/ziti/issues/3618) - Router interfaceDiscovery.minReportInterval value was being set to checkInterval\n    * [Issue #3617](https://github.com/openziti/ziti/issues/3617) - Transit router disabled flag not passed through raft command structure\n    * [Issue #3333](https://github.com/openziti/ziti/issues/3333) - Updb user-lockout triggered by successful login attempts\n    * [Issue #3599](https://github.com/openziti/ziti/issues/3599) - Add gap detection and handling to router data model\n    * [Issue #3356](https://github.com/openziti/ziti/issues/3356) - Add WWW-Authenticate Headers\n    * [Issue #3074](https://github.com/openziti/ziti/issues/3074) - Dynamic cost range is too limited\n    * [Issue #3558](https://github.com/openziti/ziti/issues/3558) - terminator cost increased on egress dial success, not on circuit completion\n    * [Issue #3556](https://github.com/openziti/ziti/issues/3556) - global circuit costs not cleared when terminator is deleted\n    * [Issue #3557](https://github.com/openziti/ziti/issues/3557) - costing calculation for the weighted terminator selection strategy  is incorrect\n    * [Issue #2512](https://github.com/openziti/ziti/issues/2512) - Return circuit ID and error in dial failures\n    * [Issue #3569](https://github.com/openziti/ziti/issues/3569) - Version 2.0+ routers should not connect to controllers which do not support JWT formatted legacy sessions\n    * [Issue #3565](https://github.com/openziti/ziti/issues/3565) - Link dialer save 'is first conn' true, so all dials claim to be first, causing potential race condition\n    * [Issue #3575](https://github.com/openziti/ziti/issues/3575) - OIDC token endpoint code bugs possibly resulting in panics/eof errors\n    * [Issue #3550](https://github.com/openziti/ziti/issues/3550) - Support multi-underlay control channels\n    * [Issue #3535](https://github.com/openziti/ziti/issues/3535) - Remove the legacy xgress_edge_tunnel implementation\n    * [Issue #3547](https://github.com/openziti/ziti/issues/3547) - Add support for sending the dialing identity id and name to the hosting sdk\n    * [Issue #3541](https://github.com/openziti/ziti/issues/3541) - Remove option to disable the router data model in the controller\n    * [Issue #3540](https://github.com/openziti/ziti/issues/3540) - Handle UDP difference between proxy and tproxy implementations\n    * [Issue #3527](https://github.com/openziti/ziti/issues/3527) - ER/T UDP tunnels keep closed connections for 30s, preventing potential new good connections in that time\n    * [Issue #3526](https://github.com/openziti/ziti/issues/3526) - ER/T half-close logic is incorrect\n    * [Issue #3524](https://github.com/openziti/ziti/issues/3524) - Provide more error context to SDKs for terminator errors\n    * [Issue #3509](https://github.com/openziti/ziti/issues/3509) - Enforce policy on the router for oidc sessions, by closing open circuits and terminators when service access is lost\n    * [Issue #3531](https://github.com/openziti/ziti/issues/3531) - Remove created/updated/deleted terminator events. Obsoleted by entity change events.\n    * [Issue #3532](https://github.com/openziti/ziti/issues/3532) - Removed deprecated create identity <type> subcommands\n    * [Issue #3521](https://github.com/openziti/ziti/issues/3521) - Cleanup CLI to remove calls to os.Exit to be embed friendlier\n    * [Issue #3516](https://github.com/openziti/ziti/issues/3516) - Remove support for create terminator v1\n    * [Issue #3512](https://github.com/openziti/ziti/issues/3512) - Remove legacy link management code from the controller\n    * [Issue #3511](https://github.com/openziti/ziti/issues/3511) - router proxy mode fails to resolve interface if binding is 0.0.0.0\n    * [Issue #3503](https://github.com/openziti/ziti/issues/3503) - Allow routers to request current cluster membership information\n    * [Issue #3501](https://github.com/openziti/ziti/issues/3501) - Get cluster membership information from raft directly, rather than trying to cache it in the DB\n    * [Issue #3500](https://github.com/openziti/ziti/issues/3500) - Set a router data model timeline when initializing a new HA setup, rather than letting it stay blank\n    * [Issue #3504](https://github.com/openziti/ziti/issues/3504) - Reduce router data model full state updates\n    * [Issue #3492](https://github.com/openziti/ziti/pull/3492) - Bump openziti/ziti-console-assets from 3.12.9 to 4.0.0 in /dist/docker-images/ziti-controller in the all group\n    * [Issue #3484](https://github.com/openziti/ziti/issues/3484) - router ctrl channel handler for handling cluster changes has an initialization race condition\n    * [Issue #3477](https://github.com/openziti/ziti/issues/3477) - Optionally enable model changes triggered by login to be non-blocking and to be droppable if the system is under load\n    * [Issue #3473](https://github.com/openziti/ziti/issues/3473) - Enable tls handshake rate limiter by default and tweak default values.\n    * [Issue #3471](https://github.com/openziti/ziti/issues/3471) - Go tunneler is ignoring host config MaxConnections\n    * [Issue #3469](https://github.com/openziti/ziti/issues/3469) - Only send model updates on resubscribe if the RDM index has advanced\n    * [Issue #2573](https://github.com/openziti/ziti/issues/2573) - An edge router in a tight restart loop causes a resource leak on routers to which it connects.\n    * [Issue #3430](https://github.com/openziti/ziti/issues/3430) - Add permissions list to identity\n    * [Issue #2109](https://github.com/openziti/ziti/issues/2109) - Add Edge Management Read Only Capability\n    * [Issue #3435](https://github.com/openziti/ziti/issues/3435) - Add edge management API permissions by entity type and action\n    * [Issue #3441](https://github.com/openziti/ziti/issues/3441) - Update router connection tracker to interrogate active connections\n    * [Issue #3451](https://github.com/openziti/ziti/issues/3451) - ci - compare only stable releases when promoting\n    * [Issue #3437](https://github.com/openziti/ziti/issues/3437) - SDK OIDC token updates to routers should return an error if invalid\n    * [Issue #3444](https://github.com/openziti/ziti/issues/3444) - Check api session types during OIDC token updates to avoid nil reference on mixed-auth identities\n    * [Issue #3348](https://github.com/openziti/ziti/issues/3348) - Unable to clear/reset the \"tags\" property on an entity to an empty object\n    * [Issue #3452](https://github.com/openziti/ziti/issues/3452) - `ziti agent cluster add` has bad behavior if the add address doesn't match the advertise address\n    * [Issue #3410](https://github.com/openziti/ziti/issues/3410) - Consolidate fabric REST API code with edge management and edge client code\n    * [Issue #3425](https://github.com/openziti/ziti/issues/3425) - RDM not properly responding to tunneler enabled flag changes\n    * [Issue #3420](https://github.com/openziti/ziti/issues/3420) - The terminator id cache uses the same id for all terminators in a host.v2 config, resulting in a single terminator\n    * [Issue #3419](https://github.com/openziti/ziti/issues/3419) - When using the router data model, precedence specified on the per-service identity mapping are incorrectly interpreted\n    * [Issue #3318](https://github.com/openziti/ziti/issues/3318) - Terminator creation seems to slow exponentially as the number of terminators rises from 10k to 20k to 40k\n    * [Issue #3407](https://github.com/openziti/ziti/issues/3407) - The CLI doesn't properly pass JWT authentication information to websocket endpoints\n    * [Issue #3359](https://github.com/openziti/ziti/issues/3359) - Ensure router data model subscriptions have reasonable performance and will scale\n    * [Issue #3354](https://github.com/openziti/ziti/issues/3354) - SDK/ENV Info from SDKs is not distributed in HA\n    * [Issue #3381](https://github.com/openziti/ziti/issues/3381) - the fabric service REST apis are missing the maxIdleTime property\n    * [Issue #3382](https://github.com/openziti/ziti/issues/3382) - Legacy service sessions generated pre-1.7.x are incompatible with v1.7.+ and need to be cleared\n    * [Issue #3339](https://github.com/openziti/ziti/issues/3339) - get router ctrl.endpoint from ctrls claim in JWT\n    * [Issue #3378](https://github.com/openziti/ziti/issues/3378) - login with file stopped working\n    * [Issue #3349](https://github.com/openziti/ziti/issues/3349) - UPDB OIDC login returns wrong content type\n    * [Issue #2324](https://github.com/openziti/ziti/issues/2324) - Add Ext-JWT/OIDC enrollment\n    * [Issue #3346](https://github.com/openziti/ziti/issues/3346) - Fix confusing attempt logging\n    * [Issue #3337](https://github.com/openziti/ziti/issues/3337) - Router reports \"no xgress edge forwarder for circuit\"\n    * [Issue #3345](https://github.com/openziti/ziti/issues/3345) - Clean up connect events tests and remove global XG registry\n    * [Issue #3264](https://github.com/openziti/ziti/issues/3264) - Allow routers to generate alert events in cases of service misconfiguration\n    * [Issue #3321](https://github.com/openziti/ziti/issues/3321) - Health Check API missing base path on discovery endpoint\n    * [Issue #3323](https://github.com/openziti/ziti/issues/3323) - router/tunnel static services fail to bind unless new param protocol is defined\n    * [Issue #3309](https://github.com/openziti/ziti/issues/3309) - Detect link connections meant for another router\n    * [Issue #3286](https://github.com/openziti/ziti/issues/3286) - edge-api binding doesn't have the correct path on discovery endpoints\n    * [Issue #3297](https://github.com/openziti/ziti/issues/3297) - stop promoting hotfixes downstream\n    * [Issue #3295](https://github.com/openziti/ziti/issues/3295) - make ziti tunnel service:port pairs optional\n    * [Issue #3291](https://github.com/openziti/ziti/issues/3291) - replace decommissioned bitnami/kubectl\n    * [Issue #3277](https://github.com/openziti/ziti/issues/3277) - Router can deadlock on closing a connection if the incoming data channel is full\n    * [Issue #3269](https://github.com/openziti/ziti/issues/3269) - Add host-interfaces config type\n    * [Issue #3258](https://github.com/openziti/ziti/issues/3258) - Add config type proxy.v1 so proxies can be defined dynamically for the ER/T\n    * [Issue #3259](https://github.com/openziti/ziti/issues/3259) - Interfaces config type not added due to wrong name\n    * [Issue #3265](https://github.com/openziti/ziti/issues/3265) - Forwarding errors should log at debug, since they are usual part of circuit teardown\n    * [Issue #3261](https://github.com/openziti/ziti/issues/3261) - ER/T dialed xgress connections may only half-close when peer is fully closed\n\n\n","mentions_count":3},{"url":"https://api.github.com/repos/openziti/ziti/releases/320842068","assets_url":"https://api.github.com/repos/openziti/ziti/releases/320842068/assets","upload_url":"https://uploads.github.com/repos/openziti/ziti/releases/320842068/assets{?name,label}","html_url":"https://github.com/openziti/ziti/releases/tag/v2.0.0-pre14","id":320842068,"author":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"node_id":"RE_kwDODVFMN84TH6lU","tag_name":"v2.0.0-pre14","target_commitish":"main","name":"v2.0.0-pre14","draft":false,"immutable":false,"prerelease":true,"created_at":"2026-05-11T23:43:28Z","updated_at":"2026-05-11T23:51:03Z","published_at":"2026-05-11T23:51:03Z","assets":[{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/417874918","id":417874918,"node_id":"RA_kwDODVFMN84Y6EPm","name":"checksums.sha256.txt","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"text/plain; charset=utf-8","state":"uploaded","size":798,"digest":"sha256:e05ebf38df88c9bf1a12ab8a07f8869ba19835bcb4264f590dbefd833322d7bd","download_count":8,"created_at":"2026-05-11T23:51:01Z","updated_at":"2026-05-11T23:51:01Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre14/checksums.sha256.txt"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/417874917","id":417874917,"node_id":"RA_kwDODVFMN84Y6EPl","name":"sbom-v2.0.0-pre14.spdx.json","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/json","state":"uploaded","size":984865,"digest":"sha256:efc397b16d4e89ac50310778e2b1942175c69095ac94a389d167a9e5c5519fbc","download_count":4,"created_at":"2026-05-11T23:51:01Z","updated_at":"2026-05-11T23:51:01Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre14/sbom-v2.0.0-pre14.spdx.json"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/417874916","id":417874916,"node_id":"RA_kwDODVFMN84Y6EPk","name":"source-v2.0.0-pre14.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":3997933,"digest":"sha256:596355bdba0af39e6e05ca86220cc5e2437fb72a031bf63180eb9aa05fa25e94","download_count":5,"created_at":"2026-05-11T23:51:01Z","updated_at":"2026-05-11T23:51:01Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre14/source-v2.0.0-pre14.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/417874858","id":417874858,"node_id":"RA_kwDODVFMN84Y6EOq","name":"ziti-darwin-amd64-2.0.0-pre14.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":54725024,"digest":"sha256:2aa9ff6732d004cfca9a0367d0342d64877288d054ec4414a05450e51e10a3d5","download_count":8,"created_at":"2026-05-11T23:50:55Z","updated_at":"2026-05-11T23:51:01Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre14/ziti-darwin-amd64-2.0.0-pre14.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/417874854","id":417874854,"node_id":"RA_kwDODVFMN84Y6EOm","name":"ziti-darwin-arm64-2.0.0-pre14.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":50990589,"digest":"sha256:b388f510793255e38ffc1c69ea5f0cfda9a286e6c667e8b136aa59fe2ad9e6b7","download_count":5,"created_at":"2026-05-11T23:50:55Z","updated_at":"2026-05-11T23:51:00Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre14/ziti-darwin-arm64-2.0.0-pre14.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/417874861","id":417874861,"node_id":"RA_kwDODVFMN84Y6EOt","name":"ziti-linux-amd64-2.0.0-pre14.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":56041281,"digest":"sha256:4819c45480616667a8aea59b48e976feb90ea05d8efd7653d299233d57a5880d","download_count":48,"created_at":"2026-05-11T23:50:55Z","updated_at":"2026-05-11T23:51:00Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre14/ziti-linux-amd64-2.0.0-pre14.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/417874860","id":417874860,"node_id":"RA_kwDODVFMN84Y6EOs","name":"ziti-linux-arm-2.0.0-pre14.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":52446870,"digest":"sha256:34059a2e4314569162df6c6a3b02f99de6a4727977df9163e3a9462af0baaa8a","download_count":8,"created_at":"2026-05-11T23:50:55Z","updated_at":"2026-05-11T23:51:01Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre14/ziti-linux-arm-2.0.0-pre14.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/417874910","id":417874910,"node_id":"RA_kwDODVFMN84Y6EPe","name":"ziti-linux-arm64-2.0.0-pre14.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":52482894,"digest":"sha256:c4941345c12cc4a390086696ff664097e1bc46c5610ff751e373a5a4f489c25e","download_count":10,"created_at":"2026-05-11T23:51:00Z","updated_at":"2026-05-11T23:51:02Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre14/ziti-linux-arm64-2.0.0-pre14.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/417874855","id":417874855,"node_id":"RA_kwDODVFMN84Y6EOn","name":"ziti-windows-amd64-2.0.0-pre14.zip","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/zip","state":"uploaded","size":45307145,"digest":"sha256:27a522e0932c882df786110d94b057f37dca5a2358eeadfefe1d22b25ae560c8","download_count":15,"created_at":"2026-05-11T23:50:55Z","updated_at":"2026-05-11T23:51:00Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre14/ziti-windows-amd64-2.0.0-pre14.zip"}],"tarball_url":"https://api.github.com/repos/openziti/ziti/tarball/v2.0.0-pre14","zipball_url":"https://api.github.com/repos/openziti/ziti/zipball/v2.0.0-pre14","body":"# Release 2.0.0\n\n## What's New\n\nThis is the next major version release of OpenZiti, following the 1.0 release in April 2024.\nOf particular note is that HA controllers are now considered ready for general use.\nThis release also introduces a new permissions model, OIDC/JWT token-based enrollment, \nclustering performance improvements, and a number of other features and fixes. Because \nsome of these changes are not backwards compatible with older routers, we're marking this \nas a major version bump.\n\n### HA Controllers are now considered ready for general use\n\nThis is a pretty big milestone and marks the completion of work that's been ongoing for a couple of years.\nThe HA work has brought with it some notable changes. Authentication now uses JWTs by default. Using JWTs\nmeans the controllers don't need to store session and propagate them to the routers. This removes a bottleneck\nfrom the network and allows the load to be more easily distributed among controllers and routers.\n\nTo support distributed authentication, the routers now get a bespoke version of the data model. In addition\nto enabling distributed authentication this will allow us to remove the need for service polling and further \nreduce the load on controllers in the future.\n\n### Router Compatibility\n\nRelated to the JWT work, routers with version 2.+ will only work with controllers that are version 2.+. This means\nto upgrade your network, controllers should be upgraded first. Routers can then be upgraded individually.\n\n2.x routers should still work fine with older router versions.\n\nWe try very hard to avoid breaking changes like this, but sometimes the engineering trade-offs lead there. This change\nwas first made in the 1.7 release. That release has not been marked stable, and we have no plans to do so, because\nof the backwards incompatibility. \n\nIf you need to support in the 1.6.12+ range, see the section \"OIDC enabled by default\".\n\n### New Permissions Model (BETA)\n\nAs one feature goes out of beta, another arrives into beta. This release introduces a new permissions system\nfor more fine grained control to the management API. It's not expected to change, but may do so based on feedback\nfrom users.\n\n### CLI Reorganization\n\nThe `ziti` CLI has been reorganized to consolidate commands that were previously \nspread across `ziti edge` and `ziti fabric` into unified top-level commands. Entity \nmanagement is now available directly via `ziti create`, `ziti delete`, `ziti list`, \nand `ziti update`. Session management has been simplified with top-level `ziti login`. \nThe existing `ziti edge` and `ziti fabric` command trees remain available.\n\n**Breaking change:** `ziti create ca` now creates a Ziti edge Certificate Authority \n(previously it created a PKI CA). PKI CA creation is still available via `ziti pki create ca`.\n\nSee [CLI Reorganization Details](#cli-reorganization-details) for the full command mapping.\n\n### Updated Release Process\n\nWe have moved to creating `-preN` releases for major and minor versions. This way we can put out release candidates,\nor feature previews and put them through internal testing and let interested folks from the community try them out.\nThen, when we're ready, we can run the full validation suite against the last pre-release and retag it. \n\nPatch releases won't have `-preN` and should contain only high priority bug fixes.\n\n### Deprecation Cleanup\n\nSince we already have a breaking change, we're removing some other backwards compatibility code.\n\n* Controller managed links \n    * Router managed links were introduced in v0.30.0. \n    * If you're upgrading from an older versions, you'll want to upgrade to the latest 1.x release before jumping to 2.x\n    * Github tracking issue: https://github.com/openziti/ziti/issues/3512\n* `ziti edge create identity <type>`\n    * Identity types other than router were removed in v0.30.2\n    * The `type` can be dropped from the CLI command\n    * Github tracking issue: https://github.com/openziti/ziti/issues/3532\n* Terminator create/update/delete events\n    * These have been superseded by entity change events, which also have create/update/delete events for terminators\n    * Entity change events were introduced in v0.28.0\n    * Github tracking issue: https://github.com/openziti/ziti/issues/3531\n* `xgress_edge_tunnel` v1\n    * This is the first implementation of the tunneler in edge-router code (ER/T) which used legacy api sessions and services\n    * The v2 version uses the router data model and was introduced in v0.30.x\n    * Github tracking issue: https://github.com/openziti/ziti/issues/3516\n* Service policy filter `type = 1` / `type = 2`\n    * Service policy list queries now expect the string form (`type = \"Dial\"`, `type = \"Bind\"`) matching the REST API\n    * The integer form was an undocumented side effect of the internal storage format and never worked with the documented filter names\n    * Github tracking issue: https://github.com/openziti/ziti/issues/3818\n\n### Generic SPA Hosting\n\nThe controller's web layer now supports hosting arbitrary single-page applications via a generic\n`binding: spa` API. Each `binding: spa` entry takes a `path` (which becomes the URL context root) and\na `location` (the directory to serve). Multiple SPAs can be registered side by side at different\ncontext roots. Example:\n\n```yaml\n- binding: spa\n  options:\n    path: zac\n    location: /opt/openziti/share/console\n    indexFile: index.html\n- binding: spa\n  options:\n    path: my-app\n    location: /opt/my-app\n    indexFile: index.html\n```\n\nThe previous `binding: zac` is preserved as a back-compat shim and continues to work without\nmodification, but emits a deprecation warning at startup. New deployments should prefer\n`binding: spa` with an explicit `path`. The legacy shim retains a global `/assets/*` URL capture so\nZAC bundles built with absolute asset paths keep working; new SPAs bound via `binding: spa` are\nexpected to use relative URLs (or be built with `<base href>` matching their `path`).\n\nThe SPA file-serving handler also gained defense-in-depth path-traversal checks (boundary-aware\nprefix matching plus a `filepath.Rel`-based containment check on every served file) so that a\ncrafted URL cannot escape the configured `location` even if the standard library's own protections\never change.\n\n### Legacy Session Deprecation\n\nOIDC sessions are now preferred. They are the default, or will become the default for SDKs and tunnelers. They are also required\nwhen running HA. Legacy API and service session are now deprecated and will be removed in the OpenZiti v3.0.0 release. \n\n### Additional Features\n\n* Controllers can now optionally bind APIs using an OpenZiti identity\n* `ziti edge login` now supports the `--network-identity` flag to authenticate and establish connections through the Ziti overlay network\n* `ziti edge login` now supports using a bearer token with `--token` for authentication. The token is expected to be\n  provided as just the JWT, not with the \"Bearer \" prefix\n* Identity configuration can now be loaded from files or environment variables for flexible deployment scenarios\n* Identities can now be provisioned just-in-time through OIDC/JWT token-based enrollment\n* Multiple model updates can now be in-flight at the same time, improving clustering performance\n* Authentication-related model updates can now be non-blocking and even dropped if the system is too busy\n* Routers now provide more error context to SDKs for terminator errors, enabling better retry behavior\n* New `proxy.v1` config type for dynamic service proxies (originally released in 1.7.0)\n* New alert event type for surfacing operational issues to network operators - Beta (originally released in 1.7.0)\n* New Azure Service Bus event sink for streaming controller events, contributed by @ffaraone (originally released in 1.7.0)\n* Bundled ZAC upgraded to 4.0\n* Build updated to Go 1.25\n* CLI cleaned up to remove calls to `os.Exit`, making it more friendly for embedding\n* OIDC is now enabled by default\n* Controller Edge APIs now return `WWW-Authenticate` response headers on `401 Unauthorized` responses, giving clients actionable information about which auth methods are accepted and what went wrong\n* HA Controllers can be marked as 'preferredLeader' via config\n* Dynamic cost range for smart routing expanded beyond the previous 64K limit\n* Dial failures now return the circuit ID and error information for easier debugging\n* Router-to-controller control channels now support multiple underlays with priority-based message routing\n* The dialing identity's ID and name are now forwarded to the hosting SDK\n* Controllers can now dial routers to establish control channels, enabling connectivity when routers are behind firewalls (Beta)\n* Refresh-token revocations are now batched and best-effort, removing the database/raft bottleneck on token refreshes\n* [OIDC discovery endpoint extensions](#oidc-discovery-endpoint-extensions) - OpenZiti-specific endpoint URLs in the OIDC discovery document\n* `ziti edge quickstart` now always runs in HA mode. The `ha` subcommand has been removed. Use\n  `ziti edge quickstart join` to add additional members to the cluster. Note: existing quickstart instances\n  are not compatible with the new HA-only mode and will need to be recreated.\n* The `--clustered` flag on `ziti create config controller` has been removed; the generated config is always\n  cluster-ready. If you have scripts passing `--clustered`, remove it.\n* [Connect events pool](#connect-events-pool) - fixes a goroutine leak when routers reconnect and ensures per-router event ordering\n* [Multiple DNS upstreams](#multiple-dns-upstreams) - the tunneler can now fan out recursive queries to several upstream resolvers in parallel\n* [OIDC CSR authentication](#oidc-csr-authentication) - identities can submit a CSR during OIDC authentication to obtain a session-bound certificate for mTLS channel communication\n\n## OIDC Discovery Endpoint Extensions\n\nThe OIDC discovery document (`/.well-known/openid-configuration`) now includes a vendor-specific\n`openziti_endpoints` field. This lets SDKs discover OpenZiti's custom login and MFA endpoints at\nruntime instead of hardcoding paths.\n\nThe field contains absolute URLs for each endpoint, derived from the issuer the client connected to:\n\n```json\n{\n  \"issuer\": \"https://controller.example.com:1280/oidc\",\n  \"authorization_endpoint\": \"https://controller.example.com:1280/oidc/authorize\",\n  \"token_endpoint\": \"https://controller.example.com:1280/oidc/oauth/token\",\n  \"...other standard OIDC fields...\",\n  \"openziti_endpoints\": {\n    \"password\":           \"https://controller.example.com:1280/oidc/login/password\",\n    \"cert\":               \"https://controller.example.com:1280/oidc/login/cert\",\n    \"ext_jwt\":            \"https://controller.example.com:1280/oidc/login/ext-jwt\",\n    \"totp\":               \"https://controller.example.com:1280/oidc/login/totp\",\n    \"totp_enroll\":        \"https://controller.example.com:1280/oidc/login/totp/enroll\",\n    \"totp_enroll_verify\": \"https://controller.example.com:1280/oidc/login/totp/enroll/verify\",\n    \"auth_queries\":       \"https://controller.example.com:1280/oidc/login/auth-queries\"\n  }\n}\n```\n\n| Key                | Method(s)   | Description                                       |\n|--------------------|-------------|---------------------------------------------------|\n| `password`         | POST        | Username/password authentication                  |\n| `cert`             | POST        | Client certificate authentication                 |\n| `ext_jwt`          | POST        | External JWT authentication                       |\n| `totp`             | POST        | TOTP code verification for MFA                    |\n| `totp_enroll`      | POST/DELETE | Start (POST) or delete (DELETE) TOTP enrollment   |\n| `totp_enroll_verify`| POST       | Verify a TOTP enrollment code                     |\n| `auth_queries`     | GET         | Retrieve pending authentication queries           |\n\nWhen the controller serves edge-oidc on multiple web servers, each discovery response reflects\nthe issuer (and port) the client connected to.\n\n## OIDC CSR Authentication\n\nIdentities that authenticate via non-certificate methods (password, external JWT) can now submit\na CSR during OIDC authentication to obtain a session-bound certificate. This enables mTLS channel\ncommunication with edge routers for identities that would otherwise have no client certificate.\n\nCertificate-authenticated identities can also submit a CSR to obtain an additional session\ncertificate alongside their authenticating certificate.\n\n### How It Works\n\nA CSR is submitted as part of the OIDC login credentials. The controller signs it and returns\nthe certificate PEM as a `session_cert` field in the token endpoint JSON response:\n\n```json\n{\n  \"access_token\": \"eyJ...\",\n  \"token_type\": \"bearer\",\n  \"refresh_token\": \"eyJ...\",\n  \"id_token\": \"eyJ...\",\n  \"expires_in\": 1800,\n  \"session_cert\": \"-----BEGIN CERTIFICATE-----\\nMII...\"\n}\n```\n\nThe issued certificate contains a SPIFFE ID binding it to the identity and API session:\n```\nspiffe://{trustDomain}/identity/{identityId}/apiSession/{apiSessionId}/apiSessionCertificate/{certId}\n```\n\n### CSR Submission Points\n\n| Token Endpoint Grant Type | CSR Source                            | Use Case             |\n|---------------------------|---------------------------------------|----------------------|\n| Authorization Code        | `csrPem` field in login POST body     | Initial cert issue   |\n| Refresh Token             | `csr_pem` form parameter              | Cert rotation        |\n| Token Exchange            | `csr_pem` form parameter              | Cert rotation        |\n\n### Certificate Fingerprint Claims\n\nThe access token JWT includes two related claims:\n\n- `z_cfs` (CertFingerprints): all certificate fingerprints valid for this session. Contains the\n  authenticating cert fingerprint (for cert auth) and/or the CSR-issued session cert fingerprint.\n- `z_acf` (AuthCertFingerprint): the authenticating certificate fingerprint, present only for\n  certificate-authenticated sessions.\n\nOn cert rotation via refresh or token exchange, `z_cfs` is rebuilt as the auth cert fingerprint\n(if present) plus the new CSR cert fingerprint. The previous session cert fingerprint is replaced.\n\n### Certificate Binding Verification\n\nWhen a token carries `z_cfs`, the controller enforces certificate binding on the refresh token\nand token exchange endpoints:\n\n- If `z_cfs` is non-empty, the TLS leaf certificate must match at least one fingerprint in\n  `z_cfs`. Requests without a matching certificate are rejected.\n- If `z_cfs` is empty, the controller falls back to SPIFFE ID verification, checking whether\n  the leaf certificate's SAN URI references the correct API session.\n\nA session that starts without `z_cfs` can transition to having it by submitting a CSR during\na refresh. Once `z_cfs` is present, it cannot be removed.\n\n### Controller Capability\n\nControllers advertise `OIDC_AUTH_WITH_CSR` in the `/version` capabilities list when OIDC is\nenabled. SDKs can check for this capability before attempting CSR submission.\n\n### Requirements\n\n- The CSR must be a valid PEM-encoded PKCS#10 certificate request. Invalid CSRs are rejected\n  with a 400 Bad Request error in OIDC error format.\n- The controller only uses the public key from the CSR. Subject, DNS names, IP addresses,\n  email addresses, and URI SANs in the CSR are ignored.\n- Issued certificates have a one-year lifetime.\n- Only the leaf certificate fingerprint is tracked in token claims. Intermediate certificates\n  in the TLS chain are not considered.\n\n## Connect Events Pool\n\nThe controller now uses per-router, single-worker goroutine pools to process identity\nconnect/disconnect events. Previously each router connection spawned a dedicated\ngoroutine that was never cleaned up on disconnect, leaking a goroutine per reconnect\ncycle. Under churn (e.g., chaos testing with hundreds of routers) this could accumulate\ntens of thousands of leaked goroutines and destabilize the controller.\n\nUsing a single-worker pool per router also ensures that events from the same router are\nalways processed in FIFO order. Previously, a shared multi-worker pool could process a\nfull-state sync after a newer incremental event from the same router, causing identities\nto be incorrectly marked as disconnected.\n\nThe pool is configurable in the controller config file:\n\n```yaml\nconnectEvents:\n  queueSize: 5    # per-router work queue depth (default: 5)\n  idleTime:  30s  # worker idle timeout before exit (default: 30s)\n```\n\nThe defaults are suitable for most deployments. Each router's worker starts on demand\nand exits after the idle timeout, so no goroutines are held when there is no work.\n\nNote: the `minWorkers` and `maxWorkers` settings have been removed. Each router's pool\nis fixed at one worker for correctness.\n\n## Community Contributors\n\nThank you to the following community members for their contributions:\n\n* @ffaraone - Azure Service Bus event sink\n* @dmuensterer - OIDC token refresh fixes\n* @nenkoru - Controller isleader health check endpoint\n* Jan Starkl - UPDB auth attempts fix\n* Mamy Ratsimbazafy - uint16 port range fix\n\n## Basic Permission System (BETA)\n\nAdded a basic permission system that allows more control over identity access to controller management API operations. \nThis replaces the previous binary admin/non-admin model with a more flexible permission system.\n\n**NOTE:** This feature is in BETA, primarily so we can get feedback on which permissions make sense. The implementation is unlikely to change\nbut the set of exposed permissions may grow, shrink or change based on user feedback. \n\n### Permission Model\n\nThe permission system supports three levels of authorization:\n\n  1. **Global Permissions**: System-wide access levels\n     - `admin` - Full access to all operations. This is still controlled by the `isAdmin` flag on identity\n     - `admin_readonly` - Read-only access to all resources except debugging facilities inspect and validate\n\n  2. **Entity-Level Permissions**: Full CRUD access to specific entity types\n     - Granting an entity-level permission (e.g., `service`) provides complete create, read, update, and delete access for that entity type\n\n  3. **Action-Level Permissions**: Specific operation access on entity types\n     - Fine-grained control using the pattern `<entity>.<action>` (e.g., `service.read`, `identity.update`)\n     - Supports `create`, `read`, `update`, and `delete` actions per entity type\n\n### Supported Entity Permissions\n\nThe following entity-level permissions are available:\n\n- `auth-policy` - Authentication policy management\n- `ca` - Certificate Authority management\n- `config` - Configuration management\n- `config-type` - Configuration type management\n- `edge-router-policy` - Edge router policy management\n- `enrollment` - Enrollment management\n- `external-jwt-signer` - External JWT signer management\n- `identity` - Identity management\n- `posture-check` - Posture check management\n- `router` - Edge and transit router management\n- `service` - Service management\n- `service-policy` - Service policy management\n- `service-edge-router-policy` - Service edge router policy management\n- `terminator` - Terminator management\n- `ops` - Operational resources (API sessions, sessions, circuits, links, inspect and validate)\n\n### Permission Assignment\n\nPermissions are assigned to identities via the `permissions` field in the identity resource. Multiple permissions can be granted to a single identity, and permissions are additive.\n\n### Cross-Entity Operations\n\nListing related entities through an entity's endpoints requires appropriate permissions for the related entity type. For example:\n- Listing services for a service-policy requires `service.read` permission\n- Listing identities for an edge-router-policy requires `identity.read` permission\n- Listing configs for a service requires `config.read` permission\n\n**NOTE:** \nMore permissions than expected may be required when performing actions through the CLI or ZAC. Take for example, when an identity \nhas `config.create` and is attempting to create a new config. The CLI may fail if the identity doesn't have `config-type.read`\nas well because it will need to look up the config type id that corresponds to the given config type name.\n\nSimilar cross entity read permissions may be required when creating services.\n\n### Admin Protection\n\nNon-admin identities cannot:\n- Create identities with the `isAdmin` flag\n- Create identities with any permissions granted\n- Modify admin-related fields on existing identities\n- Update or delete admin identities\n- Grant permissions to identities\n\nThese protections ensure that privilege escalation is prevented and admin access remains controlled.\n\n\n## Binding Controller APIs With Identity\n\nController APIs can now be bound to an OpenZiti overlay network identity, allowing secure communication through\nthe Ziti network. This is useful for scenarios where you want to expose controller APIs only through the overlay\nnetwork rather than on a standard network interface.\n\n### Configuration Structure\n\nA standard `bindPoint` configuration looks like this:\n```text\n    bindPoints:\n      - interface: 127.0.0.1:18441\n        address: 127.0.0.1:18441\n```\n\nTo bind controller APIs to an OpenZiti identity, add an additional `identity` block to your `bindPoints`. The\nidentity configuration specifies where to load the Ziti identity file and which service to bind it to:\n\n```text\n    bindPoints:\n      - interface: 127.0.0.1:18441\n        address: 127.0.0.1:18441\n      - identity:\n          file: \"c:/temp/ctrl.testing/ctrl.identity.json\"\n          service: \"mgmt\"\n```\n\n### Supported Configuration Options\n\n- `file`: Path to a Ziti identity JSON file containing the controller's identity and enrollment certificate\n- `env`: Name of an environment variable containing a base64-encoded Ziti identity (alternative to `file`)\n- `service`: The name of the Ziti service to bind the controller API to\n\n### Using Environment Variables\n\nFor deployments where storing identity files on disk is not preferred, you can reference a base64-encoded\nidentity file from an environment variable. The environment variable should contain the base64-encoded contents\nof the identity JSON file.\n\nFor example, if an environment variable named `ZITI_CTRL_IDENTITY` contains a base64-encoded identity file:\n\n```text\n    bindPoints:\n      - interface: 127.0.0.1:18441\n        address: 127.0.0.1:18441\n      - identity:\n          env: ZITI_CTRL_IDENTITY\n          service: \"mgmt\"\n```\n\n### IPv6 Support\n\nBoth IPv4 and IPv6 addresses are supported for standard bind points. IPv6 addresses should be specified in bracket\nnotation with a port number:\n\n```text\n    bindPoints:\n      - interface: \"[::1]:18441\"\n        address: \"[::1]:18441\"\n      - identity:\n          file: \"/path/to/identity.json\"\n          service: \"mgmt\"\n```\n\n## CLI Enhancements for Identity-Based Connections\n\nThe `ziti edge login` command and REST client utilities have been enhanced to support identity-based connections\nthrough the Ziti overlay network.\n\n### New `--network-identity` Flag for `ziti edge login`\n\nThe `ziti edge login` command now includes a `--network-identity` flag that allows you to authenticate to a Ziti\ncontroller through the overlay network using a Ziti identity:\n\n```bash\nziti edge login https://ziti.mgmt.apis.local:1280 \\\n  --username myuser \\\n  --password mypass \\\n  --network-identity /path/to/identity.json\n```\n\nThis is useful when the controller is only accessible through the Ziti overlay network or when you want to ensure\nall communication to the controller flows through the overlay for security purposes.\n\n### Identity Resolution Order\n\nWhen establishing connections, identities are resolved in the following order:\n\n1. **Command-line flag**: The `--network-identity` flag takes precedence\n2. **Environment variable**: If `ZITI_CLI_NETWORK_ID` is set and contains a base64-encoded identity, it is used\n3. **Cached identity file**: If a network identity was saved from a previous login in the Ziti config directory, it may be used\n\nThis layered approach allows for flexibility in deployment scenarios:\n- Development: Use command-line flags for quick testing\n- Automation: Use environment variables in CI/CD pipelines\n- Production: Cache identities securely for repeated access\n\n#### Dialing Modes When Authenticating\n\nThe CLI supports two dialing modes:\n\n**Intercept-based Dialing (Default)**\nBy default, URLs are expected to leverage intercepts. Create a service with an appropriate intercept config and use\nthe intercept address when dialing. This is the standard mode for most use cases. For example, given a service with\nthe intercept `ziti.mgmt.apis.local`\n```bash\nziti edge login https://ziti.mgmt.apis.local:1280 \\\n  --username myuser \\\n  --password mypass \\\n  --network-identity /path/to/identity.json\n```\n\n**Identity-aware Dialing (Addressable Terminators)**\nTo support addressable terminators-based dialing, specify a user in the URL. This activates dial-by-identity\nfunctionality. The URL format should be `identity-to-dial@service-name-to-dial`. For example:\n```bash\nziti edge login https://my-identity@my-service:1280 \\\n  --username myuser \\\n  --password mypass \\\n  --network-identity /path/to/identity.json\n```\n\nIn this mode, the transport extracts the identity from the URL and uses it to establish a direct connection to\nthe specified service via the addressable terminator.\n\n\n## OIDC/JWT Token-based Enrollment\n\nOpenZiti now supports provisioning identities just-in-time through OIDC/JWT token enrollment. External identity \nproviders can be configured to allow identities to enroll using JWT tokens, with support for the resulting \nidentities to use certificate or token authentication.\n\n### External JWT Signer Configuration\n\nExternal JWT signers are configured via the Edge Management API to define enrollment behavior with the following new \nenrollment-specific properties:\n\n- **enrollToCertEnabled** - When enabled, identities can exchange a JWT token and a certificate signing request (CSR) \n        for a client certificate during enrollment. The certificate can then be used for standard certificate-based\n        authentication.\n\n- **enrollToTokenEnabled** - When enabled, identities can use a JWT token to enroll. The current token or future tokens\n        may be used for authentication.\n\n- **enrollNameClaimsSelector** - Specifies which JWT claim contains the identity name. Accepts a JSON pointer \n        (e.g., `/preferred_username`) or a simple property name (e.g., `preferred_username`, automatically converted to\n        `/preferred_username`). Defaults to `/sub` if not specified. The extracted value becomes the identity name in Ziti.\n\n- **enrollAttributeClaimsSelector** - Specifies which JWT claims to extract as identity attributes during enrollment.\n        Accepts a JSON pointer (e.g., `/roles`) or a simple property name (e.g., `roles`). Extracted attributes are \n        applied to the newly enrolled identity for use in authorization policies.\n\n- **enrollAuthPolicyId** - Specifies the authentication policy to apply to newly enrolled identities. This determines\n        what authentication methods are available for the identity post-enrollment.\n\nAdditionally the existing property named **claimsProperty** that specifies external id to match identities to:\n\n- now supports a JSON pointer (e.g., `/id`) or a simple property name (e.g., `id`)\n- is used to populate the `externalId` field of the identity\n\n### Enrollment Paths\n\n#### Certificate Enrollment (enrollToCertEnabled)\n\nWhen certificate enrollment is enabled, unauthenticated users can:\n\n1. Obtain a list of available IdPs from the public Edge Client API `GET /external-jwt-signers` endpoint, where \n   `enrollToCertEnabled` is set to `true`\n2. Obtain a JWT from the configured OIDC provider\n3. Generate a certificate signing request (CSR)\n4. Submit an enrollment request with the JWT and CSR\n5. Have their identity created in Ziti with attributes extracted from JWT claims\n6. Receive a signed client certificate for certificate-based authentication\n\n#### Token Enrollment (enrollToTokenEnabled)\n\nWhen token enrollment is enabled, unauthenticated users can:\n\n1. Obtain a list of available IdPs from the public Edge Client API `GET /external-jwt-signers` endpoint, where \n   `enrollToTokenEnabled` is set to `true`\n1. Obtain a JWT from the configured OIDC provider\n2. Submit an enrollment request with the JWT\n3. Have their identity created in Ziti with attributes extracted from JWT claims\n4. Receive a Ziti API token for token-based authentication\n\n### Edge Management API\n\nThe Edge Management API provides full CRUD operations for configuring external JWT signers:\n\n- `POST /external-jwt-signers` - Create a new external JWT signer with all configuration options\n- `GET /external-jwt-signers` - List all configured external JWT signers\n- `GET /external-jwt-signers/{id}` - Retrieve a specific signer configuration\n- `PUT /external-jwt-signers/{id}` - Update all fields of a signer\n- `PATCH /external-jwt-signers/{id}` - Partially update a signer\n- `DELETE /external-jwt-signers/{id}` - Delete a signer\n\n### Edge Client API\n\nThe Edge Client API exposes a reduced set of external JWT signer information for unauthenticated enrollment requests:\n\n- `GET /external-jwt-signers` - List available JWT signers with enrollment capabilities\n\nThe client API response includes the following fields for each signer:\n\n- `name` - Signer name\n- `externalAuthUrl` - URL where users obtain JWT tokens\n- `clientId` - OIDC client ID\n- `scopes` - Requested OIDC scopes\n- `openIdConfigurationUrl` - OIDC discovery endpoint\n- `audience` - Expected token audience\n- `targetToken` - Token type to use (ACCESS or ID)\n- **`enrollToCertEnabled`** - Flag indicating certificate enrollment is available\n- **`enrollToTokenEnabled`** - Flag indicating token enrollment is available\n\n### CLI Commands\n\n**Create an external JWT signer with enrollment options:**\n```\nziti edge controller create ext-jwt-signer <name> <issuer> \\\n  --jwks-endpoint <url> \\\n  --audience <audience> \\\n  --enroll-to-cert \\\n  --enroll-to-token=false \\\n  --enroll-name-claims-selector preferred_username \\\n  --enroll-attr-claims-selector roles \\\n  --enroll-auth-policy <policy-id-or-name>\n```\n\n**Update enrollment options on an existing signer:**\n```\nziti edge controller update ext-jwt-signer <name|id> \\\n  --enroll-to-cert \\\n  --enroll-auth-policy <policy-id-or-name>\n```\n\n**List external JWT signers:**\n```\nziti edge controller list ext-jwt-signers\n```\n\n## Clustering Performance Improvements\n\nIn previous releases, model updates were submitted to raft one at at time. This prevented \nraft from being efficient by allowing command batching. This release allows multiple \nmodel updates to be in-flight at the same time. \n\nNew Configuration Options\n\n1. Raft Apply Timeout (cluster.applyTimeout)\n\nLocation: Controller configuration file, under the cluster section\nType: Duration\nDefault: 5s\nDescription: Timeout for applying commands to the Raft distributed log. Commands that exceed this timeout will trigger adaptive rate limiter backoff.\n\nExample:\n```\ncluster:\n  applyTimeout: 10s\n```\n\n2. Cluster Rate Limiter Configuration (cluster.rateLimiter)\n\nA new adaptive rate limiter that controls the submission of commands to the Raft cluster. Unlike the existing command rate limiter, this specifically manages in-flight Raft operations with adaptive window sizing.\n\nConfiguration Structure:\n```\ncluster:\n  rateLimiter:\n    enabled: true\n    minSize: 5\n    maxSize: 250\n    timeout: 30s\n```\n\nSub-options:\n\n  - enabled (boolean)\n    - Default: true\n    - Description: Enable/disable adaptive rate limiting for Raft command submission\n  - minSize (integer)\n    - Default: 5\n    - Minimum: 1\n    - Description: Minimum window size for concurrent in-flight Raft operations\n  - maxSize (integer)\n    - Default: 250\n    - Description: Maximum window size for concurrent in-flight Raft operations. Must be >= minSize\n  - timeout (duration)\n    - Default: 30s\n    - Description: Time after which outstanding work is assumed to have failed if not marked completed\n\n3. Restart Self on Snapshot (cluster.restartSelfOnSnapshot)\n\nLocation: Controller configuration file, under cluster section\nType: Boolean\nDefault: false\nDescription: When true, the controller will automatically restart itself when restoring a snapshot to an initialized system. When false, the controller will exit with code 0, requiring external process management to restart it.\n\nExample:\n```\ncluster:\n    restartSelfOnSnapshot: true\n```\n\n### New Metrics\n\nThe adaptive rate limiter exposes three new metrics:\n\n  1. raft.rate_limiter.queue_size (gauge)\n    - Current number of operations queued/in-flight\n  2. raft.rate_limiter.work_timer (timer)\n    - Duration of rate-limited operations\n  3. raft.rate_limiter.window_size (gauge)\n    - Current adaptive window size\n\n## Rate Limiter Algorithm Improvements\n\nThe adaptive rate limiter tracker now uses a success rate metric to decide when to grow or shrink its\nconcurrency window, instead of using raw queue position. An exponentially decaying histogram tracks the\nratio of successes to backoffs. When the success rate exceeds a configurable threshold, the window is\ngrown by a multiplicative increase factor. When it falls below the threshold, the window is shrunk by a\nmultiplicative decrease factor. The check intervals for increase and decrease are independently configurable.\n\nThis approach produces smoother, more stable window adjustments under varying load, avoiding the\nover-aggressive shrinking that could occur when queue position alone was used as the signal.\n\nThis specific rate limiter implementation is used in three places:\n* **Controller TLS handshake rate limiting** - controls the rate of incoming TLS handshakes on the controller\n* **Raft command submission** - controls the rate of commands submitted to the Raft distributed log\n* **Router control channel rate limiting** - controls the rate of requests from the router to the controller\n\nNote: this work was done in advance of enabling the TLS handshake rate limiter by default. The TLS\nhandshake rate limiter is not yet enabled by default, but can be enabled via the `tls.rateLimiter`\nconfiguration section.\n\nNew configuration options (available under each `rateLimiter` section):\n\n  - successThreshold (float)\n    - Default: 0.9\n    - Description: Success rate threshold above which the window size will be increased and below which it will be decreased\n  - increaseFactor (float)\n    - Default: 1.02\n    - Description: Multiplier applied to the current window size when growing. Must be greater than 1\n  - decreaseFactor (float)\n    - Default: 0.9\n    - Description: Multiplier applied to the current window size when shrinking. Must be between 0 and 1\n  - increaseCheckInterval (integer)\n    - Default: 10\n    - Description: Number of successes between window size increase checks\n  - decreaseCheckInterval (integer)\n    - Default: 10\n    - Description: Number of backoffs between window size decrease checks\n\n## Background Processing for Identity Updates\n\nIdentity environment and authenticator updates that occur during authentication are now processed asynchronously in the background. \nThis prevents authentication requests from blocking when the system is under load, significantly improving resilience during thundering herd scenarios.\n\nWhen the background queue fills up, updates can be dropped as they will be refreshed on the next authentication attempt. \nThis allows the system to gracefully handle load spikes without impacting authentication performance.\n\nFor now, dropping entries when the queue fills will be disabled by default, but can be enabled, see below.\n\n### Configuration\n\nA new `command.background` configuration section controls the background processing behavior:\n\n```yaml\n  command:\n    background:\n      enabled: true           # Enable background processing (default: true)\n      queueSize: 1000        # Maximum queue size (default: 1000)\n      dropWhenFull: true     # Drop updates when queue is full (default: false)\n      delayThreshold: 50ms   # The threshold for how long updates are taking before starting to background updates\n```\n\nNote that the `commandRateLimiter` configuration section may instead be specified under `command` as `rateLimiter`.\n\nExample:\n\n```yaml\n  command:\n    background:\n      enabled: true\n      queueSize: 250\n      dropWhenFull: false\n      delayThreshold: 50ms\n    rateLimiter:\n      enabled:   true\n      maxQueued: 25\n```\n\nNote that if command rate limiter configuration is specified in both locations, the settings under `command` will take \nprecedence. The standalone `commandRateLimiter` section may be deprecated in the future.\n\n### Metrics\n\nWhen background processing is enabled, the following metrics are exposed:\n\n- command.background.queue_size - Current number of queued background tasks\n- command.background.worker_count - Current number of worker goroutines\n- command.background.busy_workers - Number of workers currently processing tasks\n- command.background.work_timer - Timer tracking background task execution (includes histogram, meter, and count)\n- command.background.dropped_entries - Count of dropped updates when queue is full (only when dropWhenFull is enabled)\n\n## New proxy.v1 Config Type\n\n*Originally released in 1.7.0*\n\nAdded support for dynamic service proxies with configurable binding and protocol options.\nThis allows Edge Routers and Tunnelers to create proxy endpoints that can forward traffic for Ziti services.\n\nThis differs from intercept.v1 in that intercept.v1 will intercept traffic on specified\nIP addresses or DNS entries to forward to a service using tproxy or tun interface,\ndepending on implementation.\n\nA proxy on the other hand will just start a regular TCP/UDP listener on the configured port,\nso traffic will have to be configured for that destination.\n\nExample proxy.v1 Configuration:\n\n```\n  {\n    \"port\": 8080,\n    \"protocols\": [\"tcp\"],\n    \"binding\": \"0.0.0.0\"\n  }\n```\n\nConfiguration Properties:\n  - port (required): Port number to listen on (1-65535)\n  - protocols (required): Array of supported protocols (tcp, udp)\n  - binding (optional): Interface to bind to. For the ER/T defaults to the configured lanIF config property.\n\nThis config type is currently supported by the ER/T when running in either proxy or tproxy mode.\n\n## Alert Events (BETA)\n\n*Originally released in 1.7.0*\n\nA new alert event type has been added to allow Ziti components to emit alerts for issues that network operators can address.\nAlert events are generated when components encounter problems such as service configuration errors or resource\navailability issues.\n\nAlert events include:\n  - Alert source type and ID (currently supports routers, with controller and SDK support planned for future releases)\n  - Severity level (currently supports error, with info and warning planned for future releases)\n  - Alert message and supporting details\n  - Related entities (router, identity, service, etc.) associated with the alert\n\nExample alert event when a router cannot bind a configured network interface:\n\n```\n  {\n    \"namespace\": \"alert\",\n    \"event_src_id\": \"ctrl1\",\n    \"timestamp\": \"2021-11-08T14:45:45.785561479-05:00\",\n    \"alert_source_type\": \"router\",\n    \"alert_source_id\": \"DJFljCCoLs\",\n    \"severity\": \"error\",\n    \"message\": \"error starting proxy listener for service 'test'\",\n    \"details\": [\n      \"unable to bind eth0, no address\"\n    ],\n    \"related_entities\": {\n      \"router\": \"DJFljCCoLs\",\n      \"identity\": \"DJFljCCoLs\",\n      \"service\": \"3DPjxybDvXlo878CB0X2Zs\"\n    }\n  }\n```\n\nAlert events can be consumed through the standard event system and logged to configured event handlers for monitoring and alerting purposes.\n\nThese events are currently in Beta, as the format is still subject to change. Once they've been in use in production for a while\nand proven useful, they will be marked as stable.\n\n## Azure Service Bus Event Sink\n\n*Originally released in 1.7.0. Contributed by @ffaraone.*\n\nAdds support for streaming controller events to Azure Service Bus.\nThe new logger enables real-time event streaming from the OpenZiti controller to Azure Service Bus\nqueues or topics, providing integration with Azure-based monitoring and analytics systems.\n\nTo enable the Azure Service Bus event logger, add configuration to the controller config file under the events section:\n\n```\n  events:\n    serviceBusLogger:\n      subscriptions:\n        - type: circuit\n        - type: session\n        - type: metrics\n          sourceFilter: .*\n          metricFilter: .*\n        # Add other event types as needed\n      handler:\n        type: servicebus\n        format: json\n        connectionString: \"Endpoint=sb://your-namespace.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=your-key\"\n        topic: \"ziti-events\"          # Use 'topic' for Service Bus topic\n        # queue: \"ziti-events-queue\"  # Or use 'queue' for Service Bus queue\n        bufferSize: 100                # Optional, defaults to 50\n```\n\n- Required configuration:\n    - format: Event format, currently supports only json\n    - connectionString: Azure Service Bus connection string\n    - Either topic or queue: Destination name (mutually exclusive)\n\n- Optional configuration:\n    - bufferSize: Internal message buffer size (default: 50)\n\n## OIDC is now enabled by default\n\nThe controller now automatically adds the `edge-oidc` API binding to any web listener that hosts\nthe `edge-client` API, even when `edge-oidc` is not explicitly listed in the `web` section of the\nconfiguration. This means OIDC-based authentication is available out of the box without requiring\nchanges to existing controller configurations.\n\n### Where OIDC binds\n\nThe `edge-oidc` binding is added to the same web listener(s) as `edge-client`. If `edge-client` is\nhosted on `127.0.0.1:1280`, the OIDC endpoints will be available on that same address under the\n`/oidc` path (e.g. `https://127.0.0.1:1280/oidc/.well-known/openid-configuration`).\n\nThe controller capabilities returned by `GET /version` will include `OIDC_AUTH` and the\n`edge-oidc` entry will be present in `apiVersions` when OIDC is active.\n\n### Opting out\n\nIf OIDC should not be enabled automatically, set `disableOidcAutoBinding: true` under `edge.api`:\n\n```yaml\nedge:\n  api:\n    address: 127.0.0.1:1280\n    sessionTimeout: 30m\n    disableOidcAutoBinding: true\n```\n\nWhen `disableOidcAutoBinding` is `true`, OIDC will only be active if `edge-oidc` is explicitly\nlisted as a binding in the `web` section. \n\nWhen OIDC is not enabled, all (SDK) clients will revert to using legacy authentication.\nLegacy authentication does not support multiple controllers or HA in general. Clients will treat\ntheir controller as if it is a single controller instance and will function as if no other controllers\nexist.\n\n\n## WWW-Authenticate Headers\n\nThe controller's Edge APIs now include `WWW-Authenticate` headers on `401 Unauthorized` responses,\nper issuer: https://github.com/openziti/ziti/issues/3356. These headers provide insight into why\na token was rejected. The main benefit of these headers is to convey information for JWT backed\nAPI Sessions and API Session authentication where a token may not have been provided (missing),\nis used beyond its expiration date (expired), or the token has become invalid for any other\nreason (invalid).\n\n`www-authenticate` headers are provided as a single header instance with multiple challenge values\nseparate by commas.\n\n### No Credentials Provided\n\nWhen a request hits a protected endpoint without any credentials, a single `WWW-Authenticate` header\nis returned listing both accepted auth schemes as comma-separated challenges:\n\n```\nWWW-Authenticate: zt-session realm=\"zt-session\" error=\"missing\" error_description=\"no matching token was provided\",Bearer realm=\"openziti-oidc\" error=\"missing\" error_description=\"no matching token was provided\"\n```\n\n### Token Errors\n\nWhen a token is present but cannot be accepted, the header identifies the scheme and what went wrong:\n\n```\nWWW-Authenticate: Bearer realm=\"openziti-oidc\" error=\"expired\" error_description=\"token expired\"\nWWW-Authenticate: Bearer realm=\"openziti-oidc\" error=\"invalid\" error_description=\"token is invalid\"\nWWW-Authenticate: zt-session realm=\"zt-session\" error=\"invalid\" error_description=\"token is invalid\"\n```\n\n### OIDC External JWT — Primary Authentication\n\nWhen an auth policy requires an external JWT signer for primary authentication (e.g., a PKCE flow\nbacked by an ext-jwt signer), the header identifies which signers are accepted and what went wrong.\nMultiple accepted signers are pipe-delimited in the `id` and `issuer` parameters:\n\n```\nWWW-Authenticate: Bearer realm=\"openziti-primary-ext-jwt\" error=\"missing\" error_description=\"no matching token was provided\" id=\"signer-id-1|signer-id-2\" issuer=\"https://issuer1.example.com|https://issuer2.example.com\"\n```\n\nThe `error` value follows the same `missing`/`expired`/`invalid` pattern as standard bearer token errors.\n\n### OIDC External JWT — Secondary / MFA Authentication\n\nWhen an auth policy requires an external JWT signer as a secondary factor (step-up after primary\nauth succeeds), the header identifies the single required signer. The `error` value follows the\nsame `missing`/`expired`/`invalid` pattern:\n\n```\nWWW-Authenticate: Bearer realm=\"openziti-secondary-ext-jwt\" error=\"missing\" error_description=\"no matching token was provided\" id=\"<signer-id>\" issuer=\"<issuer>\"\n```\n\n### Anonymous Endpoints\n\nUnauthenticated endpoints such as version information do not return `WWW-Authenticate` headers.\n\n## HA Preferred Leaders\n\nControllers can be marked as a preferred leader. \n\n**Example Config**\n```yaml\ncluster:\n  dataDir: /home/{{ .Model.MustVariable \"credentials.ssh.username\" }}/fablab/ctrldata\n  preferredLeader: true\n```\n\nIf a controller that is not marked preferredLeader becomes a preferredLeader, it will check \nif there's a node available that is marked as preferred. If there is one, or one later\njoins the cluster, the non-preferred node will attempt to transfer leadership to the \nnode that is marked as preferred.\n\n## Expanded Dynamic Cost Range\n\nThe dynamic cost range for smart routing has been expanded beyond the previous 64K limit. Under high\nload, terminators could saturate the cost space, making dynamic cost values meaningless for routing\ndecisions and leading to uneven load distribution. The expanded range allows for more granular cost\ndifferentiation even under heavy load.\n\n## Circuit ID and Error in Dial Failures\n\nDial failures now return the circuit ID and, when available, the error that caused the circuit to fail.\nPreviously, the circuit ID was only returned on successful dials. Note that SDKs will need to be\nupdated to surface the circuit id when a dial failure happens.\n\n## Multi-Underlay Control Channels\n\nRouter-to-controller control channels now support multiple underlays with priority-based message routing.\nThis allows time-sensitive control messages (heartbeats, routing, circuit requests) to be separated from\noperational data (metrics, inspections) across dedicated TCP connections, preventing bulk operations from\ndelaying user-affecting control plane traffic. This feature does not yet allow specifying multiple \nnetwork interfaces to use, to load balance data across.\n\n## Dialing Identity Forwarded to Hosting SDK\n\nThe identity ID and name of the dialing client are now forwarded to the hosting SDK when a circuit is\nestablished. This allows hosting applications to identify which identity initiated the connection,\nenabling identity-aware request handling on the server side. This will require SDK updates to add this\nto the API for hosting applications.\n\n## Controller-Initiated Control Channel Dials (BETA)\n\nControllers can now dial routers to establish control channels. Previously, routers were solely\nresponsible for dialing controllers. This feature is designed for deployments where one or more\ncontrollers are in a private network that routers cannot reach, but the controllers can dial out.\nA common scenario is an HA cluster where some controllers are publicly reachable and some are in\na private network. External routers connect to the public controllers normally, and the private\ncontrollers dial out to the routers.\n\n### Router Configuration\n\nRouters can configure one or more control channel listeners. Each listener specifies a bind address,\nan advertise address (reported to the controller), and optional groups for matching.\n\n```yaml\nctrl:\n  listeners:\n    - bind: tls://0.0.0.0:6262\n      advertise: tls://router.example.com:6262\n      groups:\n        - default\n```\n\nThe router is the authoritative source of ctrl channel listener information, similar to link\nlisteners. When a router connects to any controller, it reports its configured `ctrlChanListeners`\nand the controller data model is updated automatically. This means that in most deployments —\nwhere the router can reach at least one controller — no manual configuration of listener addresses\nis needed on the controller side.\n\nThe `ctrlChanListeners` field can also be set via the CLI for cases where a router cannot reach\nany controller and thus cannot initialize the data model itself:\n\n```bash\nziti edge update edge-router myRouter --bootstrap-ctrl-chan-listener 'tls://router.example.com:6262=group1,group2'\n```\n\nGroups default to `[\"default\"]` if not specified.\n\n### Controller Configuration\n\nThe controller dialer is disabled by default and must be explicitly enabled. When enabled, the\ncontroller will dial routers that have control channel listeners configured and are not already\nconnected.\n\n```yaml\nctrl:\n  dialer:\n    enabled: true\n    groups:\n      - default\n    dialDelay: 30s\n    minRetryInterval: 1s\n    maxRetryInterval: 5m\n    retryBackoffFactor: 1.5\n    fastFailureWindow: 5s\n    queueSize: 32\n    maxWorkers: 10\n```\n\n- `enabled` - Enables the controller dialer (default: `false`)\n- `groups` - List of groups to match against router listener groups (default: `[\"default\"]`)\n- `dialDelay` - Delay before the controller starts dialing after boot (default: `30s`)\n- `minRetryInterval` - Minimum backoff delay between dial retries (default: `1s`)\n- `maxRetryInterval` - Maximum backoff delay between dial retries (default: `5m`)\n- `retryBackoffFactor` - Multiplier applied to the retry delay on each failure, jittered +/- 0.5 (default: `1.5`)\n- `fastFailureWindow` - If a connection drops within this window after connecting, backoff continues rather than resetting (default: `5s`)\n- `queueSize` - Maximum number of pending dial jobs in the worker pool queue (default: `32`)\n- `maxWorkers` - Maximum number of concurrent dial workers (default: `10`)\n\nThe controller will only dial routers whose listener groups overlap with the controller's configured\ngroups. When a router advertises multiple ctrl channel listener addresses, the dialer rotates through\nthem on each failure so that an unreachable address does not block attempts to the others.\n\n### Metrics\n\nThe controller dialer worker pool exposes the following metrics under the `ctrl_channel.dialer` prefix:\n\n- `ctrl_channel.dialer.queue_size` - Current number of pending dial jobs in the queue\n- `ctrl_channel.dialer.worker_count` - Current number of dial worker goroutines\n- `ctrl_channel.dialer.busy_workers` - Number of workers currently executing a dial\n- `ctrl_channel.dialer.work_timer` - Timer tracking the duration of each dial attempt\n\n## SDK Inspection Support\n\nNew CLI commands have been added for inspecting SDK state, useful for diagnosing terminator and\nconnectivity issues.\n\n**Note:** SDK inspection requires SDK support. Currently only the Go SDK supports inspection,\nas of version 1.5.0. Other SDKs will need to add support before these commands can be used\nwith them.\n\n### `ziti fabric inspect sdk`\n\nRetrieves SDK context inspection data from identities connected to routers.\n\n```\nziti fabric inspect sdk <target-selector> <identity-id>\n```\n\nThe `<target-selector>` is a regex matching router IDs (use `.*` for all routers). The\n`<identity-id>` is the identity whose SDK context you want to inspect. The command returns\ndetailed state from the connected SDK instance, including active services, terminators, and\nconnection status.\n\n### `ziti agent tunnel dump-sdk`\n\nDumps SDK context information from a running `ziti tunnel` process via the IPC agent.\n\n```\nziti agent tunnel dump-sdk\n```\n\nReturns JSON-formatted inspection data for all SDK contexts registered in the tunnel process,\nincluding service listeners, connections, and terminator state.\n\n## Revocation System Improvements\n\nWhen a session is refreshed, the old refresh token's revocation is no longer created\nsynchronously through raft. Instead, revocations are queued in memory and flushed in\nbatches on a configurable interval. This removes the database and raft as a bottleneck\non token refreshes. If the old token is close to expiring, the revocation is skipped\nentirely.\n\nNew configuration tunables under `edge.oidc`:\n\n| Key | Default | Description |\n|-----|---------|-------------|\n| `revocationMinTokenLifetime` | unset | Skip revocation if the old token expires within this duration (must be < 50% of `refreshTokenDuration`) |\n| `revocationBucketInterval` | `1m` | Bucket window for batching revocations before flushing through raft |\n| `revocationBucketMaxSize` | `200` | Max revocations per raft entry |\n| `revocationMaxQueued` | `25000` | Max revocations queued in memory before dropping |\n| `revocationEnforcerFrequency` | `1m` | How often expired revocations are purged (leader only) |\n\n## CLI Reorganization Details\n\nThe CLI has been reorganized so that edge and fabric entity management commands are \navailable directly at the top level. The `ziti edge` and `ziti fabric` command trees \nremain fully functional — the new top-level commands are additional entry points, not \nreplacements.\n\n### Top-Level CRUD Commands\n\nEntity create, delete, list, and update operations that previously required the \n`ziti edge` or `ziti fabric` prefix are now available directly:\n\n| New command | Previous command |\n|---|---|\n| `ziti create identity ...` | `ziti edge create identity ...` |\n| `ziti delete service ...` | `ziti edge delete service ...` |\n| `ziti list edge-routers` | `ziti edge list edge-routers` |\n| `ziti update identity ...` | `ziti edge update identity ...` |\n| `ziti list circuits` | `ziti fabric list circuits` |\n| `ziti delete terminator ...` | `ziti fabric delete terminator ...` |\n\nAll edge and fabric entities are available under the consolidated commands. When an \nentity name exists in both edge and fabric (e.g., `service`, `router`), the edge \nversion is the default. Fabric equivalents are accessible with a `fabric-` prefix \n(e.g., `ziti list fabric-services`).\n\n### Top-Level Login\n\nSession management is now available at the top level:\n\n| New command | Previous command |\n|---|---|\n| `ziti login` | `ziti edge login` |\n| `ziti login forget` | `ziti edge login forget` |\n| `ziti login use` | `ziti edge use` |\n\n### Breaking Change: `ziti create ca`\n\n`ziti create ca` now creates a Ziti edge Certificate Authority, matching the \nbehavior of `ziti edge create ca`. Previously, `ziti create ca` was a PKI command \nfor generating CA certificates on the local filesystem.\n\nThe PKI command is still available at its original location:\n\n```\nziti pki create ca ...\n```\n\nScripts that use `ziti create ca` for PKI operations should be updated to use \n`ziti pki create ca` instead.\n\n## Multiple DNS Upstreams\n\nThe tunneler's DNS resolver now accepts multiple upstream DNS servers and fans out recursive queries\nto all of them in parallel, rather than being limited to a single upstream. This is useful for split-horizon\nsetups where different resolvers are authoritative for different zones, and for environments where a primary\nresolver may be slow or unreachable.\n\n### CLI\n\nThe `ziti tunnel --dnsUpstream` flag is now a repeatable string slice. Upstreams can be listed by repeating\nthe flag or by passing a comma-separated value:\n\n```bash\nziti tunnel run \\\n  --dnsUpstream udp://10.96.0.10:53 \\\n  --dnsUpstream tcp://8.8.8.8:53\n```\n\n### Router Config\n\nIn `xgress_edge_tunnel` router configs, `options.dnsUpstream` now accepts either a single string (as before)\nor a list of strings. Existing configs continue to work unchanged:\n\n```yaml\n# single upstream (unchanged)\noptions:\n  dnsUpstream: udp://10.96.0.10:53\n\n# multiple upstreams\noptions:\n  dnsUpstream:\n    - udp://10.96.0.10:53\n    - tcp://8.8.8.8:53\n```\n\n### How Resolution Works\n\nWhen a query comes in, the resolver dispatches it to every configured upstream concurrently. The first\nresponse with `RCODE=NOERROR` wins and is returned to the client immediately, so split-horizon lookups\nwork even if one upstream is slow. If no upstream returns NOERROR, the resolver picks the best-ranked\nnon-NOERROR reply (NXDOMAIN > SERVFAIL > REFUSED) so authoritative negative answers aren't masked by\ntransport failures. If every upstream fails to respond, the query is treated as unanswerable and handled\nper the configured `dnsUnanswerable` disposition.\n\n## Agent Inspect\n\nThe `ziti agent` CLI now has a generic `inspect` subcommand that works against any ziti process\n(controller, router, or tunneler) over the local IPC agent channel. It sends the inspect request\ndirectly to the target process, so unlike `ziti fabric inspect` it doesn't fan out through the\ncontroller and doesn't require network connectivity to the target.\n\n```\nziti agent inspect <value> [values...]\n```\n\nValues are matched against whatever the target process exposes. Common inspect keys:\n\n* Routers: `stackdump`, `links`, `config`, `metrics`, `sdk-terminators`, `ert-terminators`,\n  `router-circuits`, `router-data-model`, `router-controllers`\n* Controllers: `stackdump`, `config`, `metrics`, `connected-routers`, `connected-peers`,\n  `cluster-config`, `router-messaging`, `terminator-costs`, `data-model-index`\n* Tunnelers: `stackdump`, `sdk`\n\n## Current Beta Features\n\nBeta features are still under development and are subject to change. They should\nbe usable in their released form. Though unlikely, there is a small chance they will \nbe removed. \n\n* Basic Permission System\n* Alert Events\n* Controller-Initiated Control Channel Dials\n\n## Component Updates and Bug Fixes\n\n* github.com/openziti/agent: [v1.0.31 -> v1.0.33](https://github.com/openziti/agent/compare/v1.0.31...v1.0.33)\n* github.com/openziti/channel/v4: [v4.2.28 -> v4.3.11](https://github.com/openziti/channel/compare/v4.2.28...v4.3.11)\n    * [Issue #242](https://github.com/openziti/channel/issues/242) - Reconnecting channel shouldn't allow changing ids\n    * [Issue #235](https://github.com/openziti/channel/issues/235) - Bump allowed hello message headers size to 16k from 4k\n    * [Issue #228](https://github.com/openziti/channel/issues/228) - Ensure that Underlay never return nil on MultiChannel\n    * [Issue #226](https://github.com/openziti/channel/issues/226) - Allow specifying a minimum number of underlays for a channel, regardless of underlay type\n    * [Issue #225](https://github.com/openziti/channel/issues/225) - Add ChannelCreated to the UnderlayHandler API to allow handlers to be initialized with the channel before binding\n    * [Issue #224](https://github.com/openziti/channel/issues/224) - Update the underlay dispatcher to allow unknown underlay types to fall through to the default\n    * [Issue #222](https://github.com/openziti/channel/issues/222) - Allow injecting the underlay type into messages\n\n* github.com/openziti/edge-api: [v0.26.47 -> v0.28.1](https://github.com/openziti/edge-api/compare/v0.26.47...v0.28.1)\n    * [Issue #175](https://github.com/openziti/edge-api/issues/175) - ctrlChanListeners should have x-omit-empty: false attribute\n    * [Issue #170](https://github.com/openziti/edge-api/issues/170) - Add preferredLeader flag to controllers\n    * [Issue #167](https://github.com/openziti/edge-api/issues/167) - Add ctrlChanListeners to router types\n    * [Issue #164](https://github.com/openziti/edge-api/issues/164) - Add permissions list to identity\n\n* github.com/openziti/foundation/v2: [v2.0.72 -> v2.0.90](https://github.com/openziti/foundation/compare/v2.0.72...v2.0.90)\n    * [Issue #472](https://github.com/openziti/foundation/issues/472) - Add support for multi-bit set/get to AtomicBitSet\n    * [Issue #464](https://github.com/openziti/foundation/issues/464) - Add support for -pre in versions\n    * [Issue #455](https://github.com/openziti/foundation/issues/455) - Correctly close goroutine pool when external close is signaled\n    * [Issue #452](https://github.com/openziti/foundation/issues/452) - Goroutine pool with a min worker count of 1 can drop to 0 workers due to race condition\n\n* github.com/openziti/identity: [v1.0.111 -> v1.0.128](https://github.com/openziti/identity/compare/v1.0.111...v1.0.128)\n    * [Issue #68](https://github.com/openziti/identity/issues/68) - Shutdown file watcher when stopping identity watcher\n\n* github.com/openziti/metrics: [v1.4.2 -> v1.4.5](https://github.com/openziti/metrics/compare/v1.4.2...v1.4.5)\n    * [Issue #58](https://github.com/openziti/metrics/issues/58) - Add GaugeFloat64 support\n    * [Issue #56](https://github.com/openziti/metrics/issues/56) - underlying resources of reference counted meters are not cleaned up when reference count hits zero\n\n* github.com/openziti/runzmd: [v1.0.80 -> v1.0.90](https://github.com/openziti/runzmd/compare/v1.0.80...v1.0.90)\n* github.com/openziti/sdk-golang: [v1.2.3 -> v1.7.0](https://github.com/openziti/sdk-golang/compare/v1.2.3...v1.7.0)\n    * [Issue #901](https://github.com/openziti/sdk-golang/issues/901) - Move xgress back to having retransmitter goroutine per-xgress\n    * [Issue #906](https://github.com/openziti/sdk-golang/issues/906) - Fix potential nil references on session service structs\n    * [Issue #897](https://github.com/openziti/sdk-golang/issues/897) - Allow xgress to use pull model for reads when appropriate\n    * [Issue #895](https://github.com/openziti/sdk-golang/issues/895) - Limit effect sudden rtt spikes can have on rtt moving average\n    * [Issue #902](https://github.com/openziti/sdk-golang/issues/902) - Inspect response message content types are mixed up\n    * [Issue #887](https://github.com/openziti/sdk-golang/issues/887) - Fix listener manager cleanup\n    * [Issue #886](https://github.com/openziti/sdk-golang/issues/886) - When controller is busy during service refresh, backoff and retry instead of falling back to full refresh\n    * [Issue #885](https://github.com/openziti/sdk-golang/issues/885) - Only compare relevant service fields when looking for changes\n    * [Issue #884](https://github.com/openziti/sdk-golang/issues/884) - Add deadline for bind establishment\n    * [Issue #883](https://github.com/openziti/sdk-golang/issues/883) - Router level listener can be left open if multi-listener closes during listener establishment\n    * [Issue #877](https://github.com/openziti/sdk-golang/issues/877) - Handle differences in xgress eof/end-of-circuit handling by adding a capabilities exchange\n    * [Issue #832](https://github.com/openziti/sdk-golang/issues/832) - Fuzz session refresh timers\n    * [Issue #879](https://github.com/openziti/sdk-golang/issues/879) - Return the connId in inspect response\n    * [Issue #878](https://github.com/openziti/sdk-golang/issues/878) - Fix responses from rx goroutines\n    * [Issue #874](https://github.com/openziti/sdk-golang/issues/874) - Add inspect support at the context level\n    * [Issue #871](https://github.com/openziti/sdk-golang/issues/871) - Make SDK better at sticking to MaxTerminator terminators\n    * [Issue #708](https://github.com/openziti/sdk-golang/issues/708) - Support for Go's built-in context in Dial methods\n    * [Issue #860](https://github.com/openziti/sdk-golang/issues/860) - Make the dialing identity's id and name available on dialed connections\n    * [Issue #857](https://github.com/openziti/sdk-golang/issues/857) - Use new error code and retry hints to correctly react to terminator errors\n    * [Issue #847](https://github.com/openziti/sdk-golang/issues/847) - Ensure the initial version check succeeds, to ensure we don't legacy sessions on ha or oidc-enabled controllers\n    * [Issue #824](https://github.com/openziti/sdk-golang/pull/824) - release notes and hard errors on no TOTP handler breaks partial auth events\n    * [Issue #818](https://github.com/openziti/sdk-golang/issues/818) - Full re-auth should not clear services list, as that breaks the on-change logic\n    * [Issue #817](https://github.com/openziti/sdk-golang/issues/817) - goroutines can get stuck when iterating over randomized HA controller list\n    * [Issue #736](https://github.com/openziti/sdk-golang/issues/736) - Migrate from github.com/mailru/easyjson\n    * [Issue #813](https://github.com/openziti/sdk-golang/issues/813) - SDK doesn't stop close listener when it detects that a service being hosted gets deleted\n    * [Issue #811](https://github.com/openziti/sdk-golang/issues/811) - Credentials are lost when explicitly set\n    * [Issue #807](https://github.com/openziti/sdk-golang/issues/807) - Don't send close from rxer to avoid blocking\n    * [Issue #800](https://github.com/openziti/sdk-golang/issues/800) - Tidy create service session logging\n\n* github.com/openziti/secretstream: [v0.1.39 -> v0.1.49](https://github.com/openziti/secretstream/compare/v0.1.39...v0.1.49)\n* github.com/openziti/transport/v2: [v2.0.188 -> v2.0.215](https://github.com/openziti/transport/compare/v2.0.188...v2.0.215)\n    * [Issue #31](https://github.com/openziti/transport/issues/31) - ipv6 Transport Address Parsing\n    * [Issue #149](https://github.com/openziti/transport/issues/149) - Archive transwarp code\n\n* github.com/openziti/xweb/v3: [v2.3.4 -> v3.0.4](https://github.com/openziti/xweb/compare/v2.3.4...v3.0.4)\n    * [Issue #32](https://github.com/openziti/xweb/issues/32) - watched identities sometimes don't reload when changed\n\n* github.com/openziti/go-term-markdown: v1.0.1 (new)\n* github.com/openziti/ziti/v2: [v1.6.8 -> v2.0.0](https://github.com/openziti/ziti/compare/v1.6.8...v2.0.0)\n    * [Issue #3860](https://github.com/openziti/ziti/issues/3860) - Router data model index resets to 0 on controller restart\n    * [Issue #3855](https://github.com/openziti/ziti/issues/3855) - Filter current api session certs list by current api session\n    * [Issue #3838](https://github.com/openziti/ziti/issues/3838) - List controllers on management API has incorrect permissions check\n    * [Issue #3837](https://github.com/openziti/ziti/issues/3837) - Create db snapshot with path has incorrect permissions check\n    * [Issue #3846](https://github.com/openziti/ziti/issues/3846) - OIDC token binds client cert during non-cert auth, causes PoP failures\n    * [Issue #3809](https://github.com/openziti/ziti/issues/3809) - Support CSR submission during OIDC authentication for session-bound certificates\n    * [Issue #3830](https://github.com/openziti/ziti/issues/3830) - statemanager is holding on to edge connections and they're never getting cleared\n    * [Issue #3824](https://github.com/openziti/ziti/issues/3824) - Allow calling inspect using the IPC agent on the controller, router and go tunnel\n    * [Issue #2049](https://github.com/openziti/ziti/issues/2049) - The ziti agent command should have a controller connection status\n    * [Issue #3784](https://github.com/openziti/ziti/issues/3784) - Fix link registry race condition on reporting links on reconnect\n    * [Issue #3734](https://github.com/openziti/ziti/issues/3734) - Enforce client certificate proof-of-possession on controller REST API for OIDC sessions\n    * [Issue #3806](https://github.com/openziti/ziti/issues/3806) - Expose OpenZiti-specific login and MFA endpoints in the OIDC discovery document\n    * [Issue #3818](https://github.com/openziti/ziti/issues/3818) - Filtering policies by keywords `Dial` and `Bind` doesn't work\n    * [Issue #3816](https://github.com/openziti/ziti/issues/3816) - Support multiple upstream DNS providers in ziti tunnel and ER/T\n    * [Issue #3699](https://github.com/openziti/ziti/issues/3699) - Consolidate CLI edge and fabric commands in top level create/update/delete/list/login commands\n    * [Issue #3788](https://github.com/openziti/ziti/issues/3788) - OIDC Endpoints return 400 Bad Request instead of underlying error\n    * [Issue #3680](https://github.com/openziti/ziti/issues/3680) - adds revocation control to CLI and Management API\n    * [Issue #3717](https://github.com/openziti/ziti/issues/3717) - Generic error message for specific error\n    * [Issue #3543](https://github.com/openziti/ziti/issues/3543) - New Circuit Failure code for sockets not available\n    * [Issue #3364](https://github.com/openziti/ziti/issues/3364) - Make no such host error specific\n    * [Issue #2888](https://github.com/openziti/ziti/issues/2888) - New specific Error code for port not allowed\n    * [Issue #2859](https://github.com/openziti/ziti/issues/2859) - Create specific error code for DNS failed resolution\n    * [Issue #1580](https://github.com/openziti/ziti/issues/1580) - Invalid link destination should have a specific error code\n    * [Issue #3706](https://github.com/openziti/ziti/issues/3706) - Increase link payload/ack queue sizes and make them configurable\n    * [Issue #3778](https://github.com/openziti/ziti/issues/3778) - SetRouterDataModel can deadlock in the router\n    * [Issue #3777](https://github.com/openziti/ziti/issues/3777) - With the new circuit reserve, we can have circuits with no path in the controller circuit set, which can cause panics\n    * [Issue #3770](https://github.com/openziti/ziti/issues/3770) - Update Token Requests Should Close Channel Connections If Invalid\n    * [Issue #3762](https://github.com/openziti/ziti/issues/3762) - Revocations not included in full router data model state\n    * [Issue #3757](https://github.com/openziti/ziti/issues/3757) - Mesh peer signing cert from header is overwritten by TLS underlay cert\n    * [Issue #3756](https://github.com/openziti/ziti/issues/3756) - TLS handshake rate limiter timeout check reads from wrong config scope\n    * [Issue #3755](https://github.com/openziti/ziti/issues/3755) - commandHandler config read from wrong scope\n    * [Issue #3754](https://github.com/openziti/ziti/issues/3754) - dialFailed drops applyFailed parameter, preventing duplicate link retry jitter\n    * [Issue #3753](https://github.com/openziti/ziti/issues/3753) - SPIFFE trust domain prefix check has swapped HasPrefix arguments\n    * [Issue #3746](https://github.com/openziti/ziti/issues/3746) - The controller connect events control channel handler leaks a goroutine\n    * [Issue #3747](https://github.com/openziti/ziti/issues/3747) - Update controller peer error marshalling for app code changes\n    * [Issue #3721](https://github.com/openziti/ziti/issues/3721) - Add CreateCircuitV3 to controller\n    * [Issue #3719](https://github.com/openziti/ziti/issues/3719) - Allow binding specific inspects to pass through to xgress listener implementations\n    * [Issue #3696](https://github.com/openziti/ziti/issues/3696) - oidc provider is non-deterministic for wildcard certs\n    * [Issue #3681](https://github.com/openziti/ziti/issues/3681) - coalesce OIDC JWT revocations to reduce controller write pressure\n    * [Issue #3683](https://github.com/openziti/ziti/issues/3683) - add fablab test for testing flow control changes over a longer term\n    * [Issue #3673](https://github.com/openziti/ziti/issues/3673) - revocation build-up in db and rdm\n    * [Issue #3674](https://github.com/openziti/ziti/issues/3674) - Update to Go 1.26\n    * [Issue #3496](https://github.com/openziti/ziti/issues/3496) - MFA TOTP Enrollment During OIDC Authentication Does Not Work\n    * [Issue #3609](https://github.com/openziti/ziti/issues/3609) - Stabilize terminator creation test for 2.0\n    * [Issue #3648](https://github.com/openziti/ziti/issues/3648) - tunnel: myCopy logs router ID as circuitId, causing misleading debug output\n    * [Issue #3658](https://github.com/openziti/ziti/issues/3658) - Raft cluster join fails with \"hello message too big\" when using long hostnames\n    * [Issue #3626](https://github.com/openziti/ziti/issues/3626) - controller: overlay bind point produces malformed URL in /versions apiBaseUrls\n    * [Issue #3635](https://github.com/openziti/ziti/issues/3635) - Allow controllers to dial routers to support more topologies\n    * [Issue #3607](https://github.com/openziti/ziti/issues/3607) - linux installer not upgradable from v1\n    * [Issue #3650](https://github.com/openziti/ziti/issues/3650) - Reroute doesn't proactively clean up orphaned route entries\n    * [Issue #3571](https://github.com/openziti/ziti/issues/3571) - Ensure 2.0 backwards compatibility with 1.6 and 1.5 using the smoketest\n    * [Issue #3636](https://github.com/openziti/ziti/issues/3636) - Adaptive rate limiter should use success rate rather than queue position\n    * [Issue #3600](https://github.com/openziti/ziti/issues/3600) - Add a preferredLeader flag, allow selected nodes to be preferred for raft leader, if they're available\n    * [Issue #3642](https://github.com/openziti/ziti/issues/3642) - Use xgress_common.Connection type for xgress_transport and xgress_proxy\n    * [Issue #3597](https://github.com/openziti/ziti/issues/3597) - Enable OIDC API by default\n    * [Issue #3624](https://github.com/openziti/ziti/issues/3624) - Multi-underlay control channel doesn't correctly handle lack of group secret on non-grouped underlays\n    * [Issue #3613](https://github.com/openziti/ziti/issues/3613) - Initializing cluster from existing db using `db:` config settings results in panic\n    * [Issue #3620](https://github.com/openziti/ziti/issues/3620) - Controller control channel heartbeats config parsing was erroneously nested under options parsing\n    * [Issue #3619](https://github.com/openziti/ziti/issues/3619) - Router connect events full sync interval was using min/max values meant for the batch interval\n    * [Issue #3618](https://github.com/openziti/ziti/issues/3618) - Router interfaceDiscovery.minReportInterval value was being set to checkInterval\n    * [Issue #3617](https://github.com/openziti/ziti/issues/3617) - Transit router disabled flag not passed through raft command structure\n    * [Issue #3333](https://github.com/openziti/ziti/issues/3333) - Updb user-lockout triggered by successful login attempts\n    * [Issue #3599](https://github.com/openziti/ziti/issues/3599) - Add gap detection and handling to router data model\n    * [Issue #3356](https://github.com/openziti/ziti/issues/3356) - Add WWW-Authenticate Headers\n    * [Issue #3074](https://github.com/openziti/ziti/issues/3074) - Dynamic cost range is too limited\n    * [Issue #3558](https://github.com/openziti/ziti/issues/3558) - terminator cost increased on egress dial success, not on circuit completion\n    * [Issue #3556](https://github.com/openziti/ziti/issues/3556) - global circuit costs not cleared when terminator is deleted\n    * [Issue #3557](https://github.com/openziti/ziti/issues/3557) - costing calculation for the weighted terminator selection strategy  is incorrect\n    * [Issue #2512](https://github.com/openziti/ziti/issues/2512) - Return circuit ID and error in dial failures\n    * [Issue #3569](https://github.com/openziti/ziti/issues/3569) - Version 2.0+ routers should not connect to controllers which do not support JWT formatted legacy sessions\n    * [Issue #3565](https://github.com/openziti/ziti/issues/3565) - Link dialer save 'is first conn' true, so all dials claim to be first, causing potential race condition\n    * [Issue #3575](https://github.com/openziti/ziti/issues/3575) - OIDC token endpoint code bugs possibly resulting in panics/eof errors\n    * [Issue #3550](https://github.com/openziti/ziti/issues/3550) - Support multi-underlay control channels\n    * [Issue #3535](https://github.com/openziti/ziti/issues/3535) - Remove the legacy xgress_edge_tunnel implementation\n    * [Issue #3547](https://github.com/openziti/ziti/issues/3547) - Add support for sending the dialing identity id and name to the hosting sdk\n    * [Issue #3541](https://github.com/openziti/ziti/issues/3541) - Remove option to disable the router data model in the controller\n    * [Issue #3540](https://github.com/openziti/ziti/issues/3540) - Handle UDP difference between proxy and tproxy implementations\n    * [Issue #3527](https://github.com/openziti/ziti/issues/3527) - ER/T UDP tunnels keep closed connections for 30s, preventing potential new good connections in that time\n    * [Issue #3526](https://github.com/openziti/ziti/issues/3526) - ER/T half-close logic is incorrect\n    * [Issue #3524](https://github.com/openziti/ziti/issues/3524) - Provide more error context to SDKs for terminator errors\n    * [Issue #3509](https://github.com/openziti/ziti/issues/3509) - Enforce policy on the router for oidc sessions, by closing open circuits and terminators when service access is lost\n    * [Issue #3531](https://github.com/openziti/ziti/issues/3531) - Remove created/updated/deleted terminator events. Obsoleted by entity change events.\n    * [Issue #3532](https://github.com/openziti/ziti/issues/3532) - Removed deprecated create identity <type> subcommands\n    * [Issue #3521](https://github.com/openziti/ziti/issues/3521) - Cleanup CLI to remove calls to os.Exit to be embed friendlier\n    * [Issue #3516](https://github.com/openziti/ziti/issues/3516) - Remove support for create terminator v1\n    * [Issue #3512](https://github.com/openziti/ziti/issues/3512) - Remove legacy link management code from the controller\n    * [Issue #3511](https://github.com/openziti/ziti/issues/3511) - router proxy mode fails to resolve interface if binding is 0.0.0.0\n    * [Issue #3503](https://github.com/openziti/ziti/issues/3503) - Allow routers to request current cluster membership information\n    * [Issue #3501](https://github.com/openziti/ziti/issues/3501) - Get cluster membership information from raft directly, rather than trying to cache it in the DB\n    * [Issue #3500](https://github.com/openziti/ziti/issues/3500) - Set a router data model timeline when initializing a new HA setup, rather than letting it stay blank\n    * [Issue #3504](https://github.com/openziti/ziti/issues/3504) - Reduce router data model full state updates\n    * [Issue #3492](https://github.com/openziti/ziti/pull/3492) - Bump openziti/ziti-console-assets from 3.12.9 to 4.0.0 in /dist/docker-images/ziti-controller in the all group\n    * [Issue #3484](https://github.com/openziti/ziti/issues/3484) - router ctrl channel handler for handling cluster changes has an initialization race condition\n    * [Issue #3477](https://github.com/openziti/ziti/issues/3477) - Optionally enable model changes triggered by login to be non-blocking and to be droppable if the system is under load\n    * [Issue #3473](https://github.com/openziti/ziti/issues/3473) - Enable tls handshake rate limiter by default and tweak default values.\n    * [Issue #3471](https://github.com/openziti/ziti/issues/3471) - Go tunneler is ignoring host config MaxConnections\n    * [Issue #3469](https://github.com/openziti/ziti/issues/3469) - Only send model updates on resubscribe if the RDM index has advanced\n    * [Issue #2573](https://github.com/openziti/ziti/issues/2573) - An edge router in a tight restart loop causes a resource leak on routers to which it connects.\n    * [Issue #3430](https://github.com/openziti/ziti/issues/3430) - Add permissions list to identity\n    * [Issue #2109](https://github.com/openziti/ziti/issues/2109) - Add Edge Management Read Only Capability\n    * [Issue #3435](https://github.com/openziti/ziti/issues/3435) - Add edge management API permissions by entity type and action\n    * [Issue #3441](https://github.com/openziti/ziti/issues/3441) - Update router connection tracker to interrogate active connections\n    * [Issue #3451](https://github.com/openziti/ziti/issues/3451) - ci - compare only stable releases when promoting\n    * [Issue #3437](https://github.com/openziti/ziti/issues/3437) - SDK OIDC token updates to routers should return an error if invalid\n    * [Issue #3348](https://github.com/openziti/ziti/issues/3348) - Unable to clear/reset the \"tags\" property on an entity to an empty object\n    * [Issue #3452](https://github.com/openziti/ziti/issues/3452) - `ziti agent cluster add` has bad behavior if the add address doesn't match the advertise address\n    * [Issue #3410](https://github.com/openziti/ziti/issues/3410) - Consolidate fabric REST API code with edge management and edge client code\n    * [Issue #3425](https://github.com/openziti/ziti/issues/3425) - RDM not properly responding to tunneler enabled flag changes\n    * [Issue #3420](https://github.com/openziti/ziti/issues/3420) - The terminator id cache uses the same id for all terminators in a host.v2 config, resulting in a single terminator\n    * [Issue #3419](https://github.com/openziti/ziti/issues/3419) - When using the router data model, precedence specified on the per-service identity mapping are incorrectly interpreted\n    * [Issue #3318](https://github.com/openziti/ziti/issues/3318) - Terminator creation seems to slow exponentially as the number of terminators rises from 10k to 20k to 40k\n    * [Issue #3407](https://github.com/openziti/ziti/issues/3407) - The CLI doesn't properly pass JWT authentication information to websocket endpoints\n    * [Issue #3359](https://github.com/openziti/ziti/issues/3359) - Ensure router data model subscriptions have reasonable performance and will scale\n    * [Issue #3354](https://github.com/openziti/ziti/issues/3354) - SDK/ENV Info from SDKs is not distributed in HA\n    * [Issue #3381](https://github.com/openziti/ziti/issues/3381) - the fabric service REST apis are missing the maxIdleTime property\n    * [Issue #3382](https://github.com/openziti/ziti/issues/3382) - Legacy service sessions generated pre-1.7.x are incompatible with v1.7.+ and need to be cleared\n    * [Issue #3339](https://github.com/openziti/ziti/issues/3339) - get router ctrl.endpoint from ctrls claim in JWT\n    * [Issue #3378](https://github.com/openziti/ziti/issues/3378) - login with file stopped working\n    * [Issue #3349](https://github.com/openziti/ziti/issues/3349) - UPDB OIDC login returns wrong content type\n    * [Issue #2324](https://github.com/openziti/ziti/issues/2324) - Add Ext-JWT/OIDC enrollment\n    * [Issue #3346](https://github.com/openziti/ziti/issues/3346) - Fix confusing attempt logging\n    * [Issue #3337](https://github.com/openziti/ziti/issues/3337) - Router reports \"no xgress edge forwarder for circuit\"\n    * [Issue #3345](https://github.com/openziti/ziti/issues/3345) - Clean up connect events tests and remove global XG registry\n    * [Issue #3264](https://github.com/openziti/ziti/issues/3264) - Allow routers to generate alert events in cases of service misconfiguration\n    * [Issue #3321](https://github.com/openziti/ziti/issues/3321) - Health Check API missing base path on discovery endpoint\n    * [Issue #3323](https://github.com/openziti/ziti/issues/3323) - router/tunnel static services fail to bind unless new param protocol is defined\n    * [Issue #3309](https://github.com/openziti/ziti/issues/3309) - Detect link connections meant for another router\n    * [Issue #3286](https://github.com/openziti/ziti/issues/3286) - edge-api binding doesn't have the correct path on discovery endpoints\n    * [Issue #3297](https://github.com/openziti/ziti/issues/3297) - stop promoting hotfixes downstream\n    * [Issue #3295](https://github.com/openziti/ziti/issues/3295) - make ziti tunnel service:port pairs optional\n    * [Issue #3291](https://github.com/openziti/ziti/issues/3291) - replace decommissioned bitnami/kubectl\n    * [Issue #3277](https://github.com/openziti/ziti/issues/3277) - Router can deadlock on closing a connection if the incoming data channel is full\n    * [Issue #3269](https://github.com/openziti/ziti/issues/3269) - Add host-interfaces config type\n    * [Issue #3258](https://github.com/openziti/ziti/issues/3258) - Add config type proxy.v1 so proxies can be defined dynamically for the ER/T\n    * [Issue #3259](https://github.com/openziti/ziti/issues/3259) - Interfaces config type not added due to wrong name\n    * [Issue #3265](https://github.com/openziti/ziti/issues/3265) - Forwarding errors should log at debug, since they are usual part of circuit teardown\n    * [Issue #3261](https://github.com/openziti/ziti/issues/3261) - ER/T dialed xgress connections may only half-close when peer is fully closed\n\n\n","mentions_count":3},{"url":"https://api.github.com/repos/openziti/ziti/releases/319661925","assets_url":"https://api.github.com/repos/openziti/ziti/releases/319661925/assets","upload_url":"https://uploads.github.com/repos/openziti/ziti/releases/319661925/assets{?name,label}","html_url":"https://github.com/openziti/ziti/releases/tag/v2.0.0-pre13","id":319661925,"author":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"node_id":"RE_kwDODVFMN84TDadl","tag_name":"v2.0.0-pre13","target_commitish":"main","name":"v2.0.0-pre13","draft":false,"immutable":false,"prerelease":true,"created_at":"2026-05-08T17:50:23Z","updated_at":"2026-05-08T17:54:46Z","published_at":"2026-05-08T17:54:46Z","assets":[{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/415455499","id":415455499,"node_id":"RA_kwDODVFMN84Yw1kL","name":"checksums.sha256.txt","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"text/plain; charset=utf-8","state":"uploaded","size":798,"digest":"sha256:2a8d2439c07c451fb700873201ddfc70656203d466c217ea4510132e0a7fa54d","download_count":6,"created_at":"2026-05-08T17:54:43Z","updated_at":"2026-05-08T17:54:43Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre13/checksums.sha256.txt"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/415455498","id":415455498,"node_id":"RA_kwDODVFMN84Yw1kK","name":"sbom-v2.0.0-pre13.spdx.json","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/json","state":"uploaded","size":984865,"digest":"sha256:032204dd578960b5c22cf434fb0f199008743aff7a5d333aabe3a29c37f02799","download_count":3,"created_at":"2026-05-08T17:54:43Z","updated_at":"2026-05-08T17:54:43Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre13/sbom-v2.0.0-pre13.spdx.json"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/415455500","id":415455500,"node_id":"RA_kwDODVFMN84Yw1kM","name":"source-v2.0.0-pre13.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":3997717,"digest":"sha256:523fe378fb1633ebbd46d11815d25d63abbe21675c293bb72f85f858620bf9e0","download_count":4,"created_at":"2026-05-08T17:54:43Z","updated_at":"2026-05-08T17:54:44Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre13/source-v2.0.0-pre13.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/415455450","id":415455450,"node_id":"RA_kwDODVFMN84Yw1ja","name":"ziti-darwin-amd64-2.0.0-pre13.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":54725111,"digest":"sha256:d621185aac936c2212a33f46d5f61718a6bc91d9d7290ffd8dfecb4959ad1a3c","download_count":8,"created_at":"2026-05-08T17:54:40Z","updated_at":"2026-05-08T17:54:43Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre13/ziti-darwin-amd64-2.0.0-pre13.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/415455448","id":415455448,"node_id":"RA_kwDODVFMN84Yw1jY","name":"ziti-darwin-arm64-2.0.0-pre13.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":50990401,"digest":"sha256:7e28c2f34abffd8974cde703118bdd8c762b367832c4f70110cb21a79f1b38d6","download_count":4,"created_at":"2026-05-08T17:54:39Z","updated_at":"2026-05-08T17:54:42Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre13/ziti-darwin-arm64-2.0.0-pre13.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/415455452","id":415455452,"node_id":"RA_kwDODVFMN84Yw1jc","name":"ziti-linux-amd64-2.0.0-pre13.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":56041463,"digest":"sha256:c1b206145d133d79eab131e4a5e6e11960c46b6f8881ff730dde70ab72cc8403","download_count":50,"created_at":"2026-05-08T17:54:40Z","updated_at":"2026-05-08T17:54:43Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre13/ziti-linux-amd64-2.0.0-pre13.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/415455449","id":415455449,"node_id":"RA_kwDODVFMN84Yw1jZ","name":"ziti-linux-arm-2.0.0-pre13.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":52446695,"digest":"sha256:21b4e8b5f017263ee25d2a1129f6aaac502e3e7c7e92314433c9c3b10fd4b95a","download_count":5,"created_at":"2026-05-08T17:54:40Z","updated_at":"2026-05-08T17:54:43Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre13/ziti-linux-arm-2.0.0-pre13.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/415455451","id":415455451,"node_id":"RA_kwDODVFMN84Yw1jb","name":"ziti-linux-arm64-2.0.0-pre13.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":52482857,"digest":"sha256:21368880a6943c44acb271ed9dec893074ec22b1ce5679b8fe052a716a6c24d1","download_count":8,"created_at":"2026-05-08T17:54:40Z","updated_at":"2026-05-08T17:54:43Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre13/ziti-linux-arm64-2.0.0-pre13.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/415455483","id":415455483,"node_id":"RA_kwDODVFMN84Yw1j7","name":"ziti-windows-amd64-2.0.0-pre13.zip","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/zip","state":"uploaded","size":45306868,"digest":"sha256:275b5efb358d30f786679481daf1c64506f439cd96a64c316eefdfd76a2e3279","download_count":11,"created_at":"2026-05-08T17:54:42Z","updated_at":"2026-05-08T17:54:45Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre13/ziti-windows-amd64-2.0.0-pre13.zip"}],"tarball_url":"https://api.github.com/repos/openziti/ziti/tarball/v2.0.0-pre13","zipball_url":"https://api.github.com/repos/openziti/ziti/zipball/v2.0.0-pre13","body":"# Release 2.0.0\n\n## What's New\n\nThis is the next major version release of OpenZiti, following the 1.0 release in April 2024.\nOf particular note is that HA controllers are now considered ready for general use.\nThis release also introduces a new permissions model, OIDC/JWT token-based enrollment, \nclustering performance improvements, and a number of other features and fixes. Because \nsome of these changes are not backwards compatible with older routers, we're marking this \nas a major version bump.\n\n### HA Controllers are now considered ready for general use\n\nThis is a pretty big milestone and marks the completion of work that's been ongoing for a couple of years.\nThe HA work has brought with it some notable changes. Authentication now uses JWTs by default. Using JWTs\nmeans the controllers don't need to store session and propagate them to the routers. This removes a bottleneck\nfrom the network and allows the load to be more easily distributed among controllers and routers.\n\nTo support distributed authentication, the routers now get a bespoke version of the data model. In addition\nto enabling distributed authentication this will allow us to remove the need for service polling and further \nreduce the load on controllers in the future.\n\n### Router Compatibility\n\nRelated to the JWT work, routers with version 2.+ will only work with controllers that are version 2.+. This means\nto upgrade your network, controllers should be upgraded first. Routers can then be upgraded individually.\n\n2.x routers should still work fine with older router versions.\n\nWe try very hard to avoid breaking changes like this, but sometimes the engineering trade-offs lead there. This change\nwas first made in the 1.7 release. That release has not been marked stable, and we have no plans to do so, because\nof the backwards incompatibility. \n\nIf you need to support in the 1.6.12+ range, see the section \"OIDC enabled by default\".\n\n### New Permissions Model (BETA)\n\nAs one feature goes out of beta, another arrives into beta. This release introduces a new permissions system\nfor more fine grained control to the management API. It's not expected to change, but may do so based on feedback\nfrom users.\n\n### CLI Reorganization\n\nThe `ziti` CLI has been reorganized to consolidate commands that were previously \nspread across `ziti edge` and `ziti fabric` into unified top-level commands. Entity \nmanagement is now available directly via `ziti create`, `ziti delete`, `ziti list`, \nand `ziti update`. Session management has been simplified with top-level `ziti login`. \nThe existing `ziti edge` and `ziti fabric` command trees remain available.\n\n**Breaking change:** `ziti create ca` now creates a Ziti edge Certificate Authority \n(previously it created a PKI CA). PKI CA creation is still available via `ziti pki create ca`.\n\nSee [CLI Reorganization Details](#cli-reorganization-details) for the full command mapping.\n\n### Updated Release Process\n\nWe have moved to creating `-preN` releases for major and minor versions. This way we can put out release candidates,\nor feature previews and put them through internal testing and let interested folks from the community try them out.\nThen, when we're ready, we can run the full validation suite against the last pre-release and retag it. \n\nPatch releases won't have `-preN` and should contain only high priority bug fixes.\n\n### Deprecation Cleanup\n\nSince we already have a breaking change, we're removing some other backwards compatibility code.\n\n* Controller managed links \n    * Router managed links were introduced in v0.30.0. \n    * If you're upgrading from an older versions, you'll want to upgrade to the latest 1.x release before jumping to 2.x\n    * Github tracking issue: https://github.com/openziti/ziti/issues/3512\n* `ziti edge create identity <type>`\n    * Identity types other than router were removed in v0.30.2\n    * The `type` can be dropped from the CLI command\n    * Github tracking issue: https://github.com/openziti/ziti/issues/3532\n* Terminator create/update/delete events\n    * These have been superseded by entity change events, which also have create/update/delete events for terminators\n    * Entity change events were introduced in v0.28.0\n    * Github tracking issue: https://github.com/openziti/ziti/issues/3531\n* `xgress_edge_tunnel` v1\n    * This is the first implementation of the tunneler in edge-router code (ER/T) which used legacy api sessions and services\n    * The v2 version uses the router data model and was introduced in v0.30.x\n    * Github tracking issue: https://github.com/openziti/ziti/issues/3516\n* Service policy filter `type = 1` / `type = 2`\n    * Service policy list queries now expect the string form (`type = \"Dial\"`, `type = \"Bind\"`) matching the REST API\n    * The integer form was an undocumented side effect of the internal storage format and never worked with the documented filter names\n    * Github tracking issue: https://github.com/openziti/ziti/issues/3818\n\n### Generic SPA Hosting\n\nThe controller's web layer now supports hosting arbitrary single-page applications via a generic\n`binding: spa` API. Each `binding: spa` entry takes a `path` (which becomes the URL context root) and\na `location` (the directory to serve). Multiple SPAs can be registered side by side at different\ncontext roots. Example:\n\n```yaml\n- binding: spa\n  options:\n    path: zac\n    location: /opt/openziti/share/console\n    indexFile: index.html\n- binding: spa\n  options:\n    path: my-app\n    location: /opt/my-app\n    indexFile: index.html\n```\n\nThe previous `binding: zac` is preserved as a back-compat shim and continues to work without\nmodification, but emits a deprecation warning at startup. New deployments should prefer\n`binding: spa` with an explicit `path`. The legacy shim retains a global `/assets/*` URL capture so\nZAC bundles built with absolute asset paths keep working; new SPAs bound via `binding: spa` are\nexpected to use relative URLs (or be built with `<base href>` matching their `path`).\n\nThe SPA file-serving handler also gained defense-in-depth path-traversal checks (boundary-aware\nprefix matching plus a `filepath.Rel`-based containment check on every served file) so that a\ncrafted URL cannot escape the configured `location` even if the standard library's own protections\never change.\n\n### Legacy Session Deprecation\n\nOIDC sessions are now preferred. They are the default, or will become the default for SDKs and tunnelers. They are also required\nwhen running HA. Legacy API and service session are now deprecated and will be removed in the OpenZiti v3.0.0 release. \n\n### Additional Features\n\n* Controllers can now optionally bind APIs using an OpenZiti identity\n* `ziti edge login` now supports the `--network-identity` flag to authenticate and establish connections through the Ziti overlay network\n* `ziti edge login` now supports using a bearer token with `--token` for authentication. The token is expected to be\n  provided as just the JWT, not with the \"Bearer \" prefix\n* Identity configuration can now be loaded from files or environment variables for flexible deployment scenarios\n* Identities can now be provisioned just-in-time through OIDC/JWT token-based enrollment\n* Multiple model updates can now be in-flight at the same time, improving clustering performance\n* Authentication-related model updates can now be non-blocking and even dropped if the system is too busy\n* Routers now provide more error context to SDKs for terminator errors, enabling better retry behavior\n* New `proxy.v1` config type for dynamic service proxies (originally released in 1.7.0)\n* New alert event type for surfacing operational issues to network operators - Beta (originally released in 1.7.0)\n* New Azure Service Bus event sink for streaming controller events, contributed by @ffaraone (originally released in 1.7.0)\n* Bundled ZAC upgraded to 4.0\n* Build updated to Go 1.25\n* CLI cleaned up to remove calls to `os.Exit`, making it more friendly for embedding\n* OIDC is now enabled by default\n* Controller Edge APIs now return `WWW-Authenticate` response headers on `401 Unauthorized` responses, giving clients actionable information about which auth methods are accepted and what went wrong\n* HA Controllers can be marked as 'preferredLeader' via config\n* Dynamic cost range for smart routing expanded beyond the previous 64K limit\n* Dial failures now return the circuit ID and error information for easier debugging\n* Router-to-controller control channels now support multiple underlays with priority-based message routing\n* The dialing identity's ID and name are now forwarded to the hosting SDK\n* Controllers can now dial routers to establish control channels, enabling connectivity when routers are behind firewalls (Beta)\n* Refresh-token revocations are now batched and best-effort, removing the database/raft bottleneck on token refreshes\n* [OIDC discovery endpoint extensions](#oidc-discovery-endpoint-extensions) - OpenZiti-specific endpoint URLs in the OIDC discovery document\n* `ziti edge quickstart` now always runs in HA mode. The `ha` subcommand has been removed. Use\n  `ziti edge quickstart join` to add additional members to the cluster. Note: existing quickstart instances\n  are not compatible with the new HA-only mode and will need to be recreated.\n* The `--clustered` flag on `ziti create config controller` has been removed; the generated config is always\n  cluster-ready. If you have scripts passing `--clustered`, remove it.\n* [Connect events pool](#connect-events-pool) - fixes a goroutine leak when routers reconnect and ensures per-router event ordering\n* [Multiple DNS upstreams](#multiple-dns-upstreams) - the tunneler can now fan out recursive queries to several upstream resolvers in parallel\n* [OIDC CSR authentication](#oidc-csr-authentication) - identities can submit a CSR during OIDC authentication to obtain a session-bound certificate for mTLS channel communication\n\n## OIDC Discovery Endpoint Extensions\n\nThe OIDC discovery document (`/.well-known/openid-configuration`) now includes a vendor-specific\n`openziti_endpoints` field. This lets SDKs discover OpenZiti's custom login and MFA endpoints at\nruntime instead of hardcoding paths.\n\nThe field contains absolute URLs for each endpoint, derived from the issuer the client connected to:\n\n```json\n{\n  \"issuer\": \"https://controller.example.com:1280/oidc\",\n  \"authorization_endpoint\": \"https://controller.example.com:1280/oidc/authorize\",\n  \"token_endpoint\": \"https://controller.example.com:1280/oidc/oauth/token\",\n  \"...other standard OIDC fields...\",\n  \"openziti_endpoints\": {\n    \"password\":           \"https://controller.example.com:1280/oidc/login/password\",\n    \"cert\":               \"https://controller.example.com:1280/oidc/login/cert\",\n    \"ext_jwt\":            \"https://controller.example.com:1280/oidc/login/ext-jwt\",\n    \"totp\":               \"https://controller.example.com:1280/oidc/login/totp\",\n    \"totp_enroll\":        \"https://controller.example.com:1280/oidc/login/totp/enroll\",\n    \"totp_enroll_verify\": \"https://controller.example.com:1280/oidc/login/totp/enroll/verify\",\n    \"auth_queries\":       \"https://controller.example.com:1280/oidc/login/auth-queries\"\n  }\n}\n```\n\n| Key                | Method(s)   | Description                                       |\n|--------------------|-------------|---------------------------------------------------|\n| `password`         | POST        | Username/password authentication                  |\n| `cert`             | POST        | Client certificate authentication                 |\n| `ext_jwt`          | POST        | External JWT authentication                       |\n| `totp`             | POST        | TOTP code verification for MFA                    |\n| `totp_enroll`      | POST/DELETE | Start (POST) or delete (DELETE) TOTP enrollment   |\n| `totp_enroll_verify`| POST       | Verify a TOTP enrollment code                     |\n| `auth_queries`     | GET         | Retrieve pending authentication queries           |\n\nWhen the controller serves edge-oidc on multiple web servers, each discovery response reflects\nthe issuer (and port) the client connected to.\n\n## OIDC CSR Authentication\n\nIdentities that authenticate via non-certificate methods (password, external JWT) can now submit\na CSR during OIDC authentication to obtain a session-bound certificate. This enables mTLS channel\ncommunication with edge routers for identities that would otherwise have no client certificate.\n\nCertificate-authenticated identities can also submit a CSR to obtain an additional session\ncertificate alongside their authenticating certificate.\n\n### How It Works\n\nA CSR is submitted as part of the OIDC login credentials. The controller signs it and returns\nthe certificate PEM as a `session_cert` field in the token endpoint JSON response:\n\n```json\n{\n  \"access_token\": \"eyJ...\",\n  \"token_type\": \"bearer\",\n  \"refresh_token\": \"eyJ...\",\n  \"id_token\": \"eyJ...\",\n  \"expires_in\": 1800,\n  \"session_cert\": \"-----BEGIN CERTIFICATE-----\\nMII...\"\n}\n```\n\nThe issued certificate contains a SPIFFE ID binding it to the identity and API session:\n```\nspiffe://{trustDomain}/identity/{identityId}/apiSession/{apiSessionId}/apiSessionCertificate/{certId}\n```\n\n### CSR Submission Points\n\n| Token Endpoint Grant Type | CSR Source                            | Use Case             |\n|---------------------------|---------------------------------------|----------------------|\n| Authorization Code        | `csrPem` field in login POST body     | Initial cert issue   |\n| Refresh Token             | `csr_pem` form parameter              | Cert rotation        |\n| Token Exchange            | `csr_pem` form parameter              | Cert rotation        |\n\n### Certificate Fingerprint Claims\n\nThe access token JWT includes two related claims:\n\n- `z_cfs` (CertFingerprints): all certificate fingerprints valid for this session. Contains the\n  authenticating cert fingerprint (for cert auth) and/or the CSR-issued session cert fingerprint.\n- `z_acf` (AuthCertFingerprint): the authenticating certificate fingerprint, present only for\n  certificate-authenticated sessions.\n\nOn cert rotation via refresh or token exchange, `z_cfs` is rebuilt as the auth cert fingerprint\n(if present) plus the new CSR cert fingerprint. The previous session cert fingerprint is replaced.\n\n### Certificate Binding Verification\n\nWhen a token carries `z_cfs`, the controller enforces certificate binding on the refresh token\nand token exchange endpoints:\n\n- If `z_cfs` is non-empty, the TLS leaf certificate must match at least one fingerprint in\n  `z_cfs`. Requests without a matching certificate are rejected.\n- If `z_cfs` is empty, the controller falls back to SPIFFE ID verification, checking whether\n  the leaf certificate's SAN URI references the correct API session.\n\nA session that starts without `z_cfs` can transition to having it by submitting a CSR during\na refresh. Once `z_cfs` is present, it cannot be removed.\n\n### Controller Capability\n\nControllers advertise `OIDC_AUTH_WITH_CSR` in the `/version` capabilities list when OIDC is\nenabled. SDKs can check for this capability before attempting CSR submission.\n\n### Requirements\n\n- The CSR must be a valid PEM-encoded PKCS#10 certificate request. Invalid CSRs are rejected\n  with a 400 Bad Request error in OIDC error format.\n- The controller only uses the public key from the CSR. Subject, DNS names, IP addresses,\n  email addresses, and URI SANs in the CSR are ignored.\n- Issued certificates have a one-year lifetime.\n- Only the leaf certificate fingerprint is tracked in token claims. Intermediate certificates\n  in the TLS chain are not considered.\n\n## Connect Events Pool\n\nThe controller now uses per-router, single-worker goroutine pools to process identity\nconnect/disconnect events. Previously each router connection spawned a dedicated\ngoroutine that was never cleaned up on disconnect, leaking a goroutine per reconnect\ncycle. Under churn (e.g., chaos testing with hundreds of routers) this could accumulate\ntens of thousands of leaked goroutines and destabilize the controller.\n\nUsing a single-worker pool per router also ensures that events from the same router are\nalways processed in FIFO order. Previously, a shared multi-worker pool could process a\nfull-state sync after a newer incremental event from the same router, causing identities\nto be incorrectly marked as disconnected.\n\nThe pool is configurable in the controller config file:\n\n```yaml\nconnectEvents:\n  queueSize: 5    # per-router work queue depth (default: 5)\n  idleTime:  30s  # worker idle timeout before exit (default: 30s)\n```\n\nThe defaults are suitable for most deployments. Each router's worker starts on demand\nand exits after the idle timeout, so no goroutines are held when there is no work.\n\nNote: the `minWorkers` and `maxWorkers` settings have been removed. Each router's pool\nis fixed at one worker for correctness.\n\n## Community Contributors\n\nThank you to the following community members for their contributions:\n\n* @ffaraone - Azure Service Bus event sink\n* @dmuensterer - OIDC token refresh fixes\n* @nenkoru - Controller isleader health check endpoint\n* Jan Starkl - UPDB auth attempts fix\n* Mamy Ratsimbazafy - uint16 port range fix\n\n## Basic Permission System (BETA)\n\nAdded a basic permission system that allows more control over identity access to controller management API operations. \nThis replaces the previous binary admin/non-admin model with a more flexible permission system.\n\n**NOTE:** This feature is in BETA, primarily so we can get feedback on which permissions make sense. The implementation is unlikely to change\nbut the set of exposed permissions may grow, shrink or change based on user feedback. \n\n### Permission Model\n\nThe permission system supports three levels of authorization:\n\n  1. **Global Permissions**: System-wide access levels\n     - `admin` - Full access to all operations. This is still controlled by the `isAdmin` flag on identity\n     - `admin_readonly` - Read-only access to all resources except debugging facilities inspect and validate\n\n  2. **Entity-Level Permissions**: Full CRUD access to specific entity types\n     - Granting an entity-level permission (e.g., `service`) provides complete create, read, update, and delete access for that entity type\n\n  3. **Action-Level Permissions**: Specific operation access on entity types\n     - Fine-grained control using the pattern `<entity>.<action>` (e.g., `service.read`, `identity.update`)\n     - Supports `create`, `read`, `update`, and `delete` actions per entity type\n\n### Supported Entity Permissions\n\nThe following entity-level permissions are available:\n\n- `auth-policy` - Authentication policy management\n- `ca` - Certificate Authority management\n- `config` - Configuration management\n- `config-type` - Configuration type management\n- `edge-router-policy` - Edge router policy management\n- `enrollment` - Enrollment management\n- `external-jwt-signer` - External JWT signer management\n- `identity` - Identity management\n- `posture-check` - Posture check management\n- `router` - Edge and transit router management\n- `service` - Service management\n- `service-policy` - Service policy management\n- `service-edge-router-policy` - Service edge router policy management\n- `terminator` - Terminator management\n- `ops` - Operational resources (API sessions, sessions, circuits, links, inspect and validate)\n\n### Permission Assignment\n\nPermissions are assigned to identities via the `permissions` field in the identity resource. Multiple permissions can be granted to a single identity, and permissions are additive.\n\n### Cross-Entity Operations\n\nListing related entities through an entity's endpoints requires appropriate permissions for the related entity type. For example:\n- Listing services for a service-policy requires `service.read` permission\n- Listing identities for an edge-router-policy requires `identity.read` permission\n- Listing configs for a service requires `config.read` permission\n\n**NOTE:** \nMore permissions than expected may be required when performing actions through the CLI or ZAC. Take for example, when an identity \nhas `config.create` and is attempting to create a new config. The CLI may fail if the identity doesn't have `config-type.read`\nas well because it will need to look up the config type id that corresponds to the given config type name.\n\nSimilar cross entity read permissions may be required when creating services.\n\n### Admin Protection\n\nNon-admin identities cannot:\n- Create identities with the `isAdmin` flag\n- Create identities with any permissions granted\n- Modify admin-related fields on existing identities\n- Update or delete admin identities\n- Grant permissions to identities\n\nThese protections ensure that privilege escalation is prevented and admin access remains controlled.\n\n\n## Binding Controller APIs With Identity\n\nController APIs can now be bound to an OpenZiti overlay network identity, allowing secure communication through\nthe Ziti network. This is useful for scenarios where you want to expose controller APIs only through the overlay\nnetwork rather than on a standard network interface.\n\n### Configuration Structure\n\nA standard `bindPoint` configuration looks like this:\n```text\n    bindPoints:\n      - interface: 127.0.0.1:18441\n        address: 127.0.0.1:18441\n```\n\nTo bind controller APIs to an OpenZiti identity, add an additional `identity` block to your `bindPoints`. The\nidentity configuration specifies where to load the Ziti identity file and which service to bind it to:\n\n```text\n    bindPoints:\n      - interface: 127.0.0.1:18441\n        address: 127.0.0.1:18441\n      - identity:\n          file: \"c:/temp/ctrl.testing/ctrl.identity.json\"\n          service: \"mgmt\"\n```\n\n### Supported Configuration Options\n\n- `file`: Path to a Ziti identity JSON file containing the controller's identity and enrollment certificate\n- `env`: Name of an environment variable containing a base64-encoded Ziti identity (alternative to `file`)\n- `service`: The name of the Ziti service to bind the controller API to\n\n### Using Environment Variables\n\nFor deployments where storing identity files on disk is not preferred, you can reference a base64-encoded\nidentity file from an environment variable. The environment variable should contain the base64-encoded contents\nof the identity JSON file.\n\nFor example, if an environment variable named `ZITI_CTRL_IDENTITY` contains a base64-encoded identity file:\n\n```text\n    bindPoints:\n      - interface: 127.0.0.1:18441\n        address: 127.0.0.1:18441\n      - identity:\n          env: ZITI_CTRL_IDENTITY\n          service: \"mgmt\"\n```\n\n### IPv6 Support\n\nBoth IPv4 and IPv6 addresses are supported for standard bind points. IPv6 addresses should be specified in bracket\nnotation with a port number:\n\n```text\n    bindPoints:\n      - interface: \"[::1]:18441\"\n        address: \"[::1]:18441\"\n      - identity:\n          file: \"/path/to/identity.json\"\n          service: \"mgmt\"\n```\n\n## CLI Enhancements for Identity-Based Connections\n\nThe `ziti edge login` command and REST client utilities have been enhanced to support identity-based connections\nthrough the Ziti overlay network.\n\n### New `--network-identity` Flag for `ziti edge login`\n\nThe `ziti edge login` command now includes a `--network-identity` flag that allows you to authenticate to a Ziti\ncontroller through the overlay network using a Ziti identity:\n\n```bash\nziti edge login https://ziti.mgmt.apis.local:1280 \\\n  --username myuser \\\n  --password mypass \\\n  --network-identity /path/to/identity.json\n```\n\nThis is useful when the controller is only accessible through the Ziti overlay network or when you want to ensure\nall communication to the controller flows through the overlay for security purposes.\n\n### Identity Resolution Order\n\nWhen establishing connections, identities are resolved in the following order:\n\n1. **Command-line flag**: The `--network-identity` flag takes precedence\n2. **Environment variable**: If `ZITI_CLI_NETWORK_ID` is set and contains a base64-encoded identity, it is used\n3. **Cached identity file**: If a network identity was saved from a previous login in the Ziti config directory, it may be used\n\nThis layered approach allows for flexibility in deployment scenarios:\n- Development: Use command-line flags for quick testing\n- Automation: Use environment variables in CI/CD pipelines\n- Production: Cache identities securely for repeated access\n\n#### Dialing Modes When Authenticating\n\nThe CLI supports two dialing modes:\n\n**Intercept-based Dialing (Default)**\nBy default, URLs are expected to leverage intercepts. Create a service with an appropriate intercept config and use\nthe intercept address when dialing. This is the standard mode for most use cases. For example, given a service with\nthe intercept `ziti.mgmt.apis.local`\n```bash\nziti edge login https://ziti.mgmt.apis.local:1280 \\\n  --username myuser \\\n  --password mypass \\\n  --network-identity /path/to/identity.json\n```\n\n**Identity-aware Dialing (Addressable Terminators)**\nTo support addressable terminators-based dialing, specify a user in the URL. This activates dial-by-identity\nfunctionality. The URL format should be `identity-to-dial@service-name-to-dial`. For example:\n```bash\nziti edge login https://my-identity@my-service:1280 \\\n  --username myuser \\\n  --password mypass \\\n  --network-identity /path/to/identity.json\n```\n\nIn this mode, the transport extracts the identity from the URL and uses it to establish a direct connection to\nthe specified service via the addressable terminator.\n\n\n## OIDC/JWT Token-based Enrollment\n\nOpenZiti now supports provisioning identities just-in-time through OIDC/JWT token enrollment. External identity \nproviders can be configured to allow identities to enroll using JWT tokens, with support for the resulting \nidentities to use certificate or token authentication.\n\n### External JWT Signer Configuration\n\nExternal JWT signers are configured via the Edge Management API to define enrollment behavior with the following new \nenrollment-specific properties:\n\n- **enrollToCertEnabled** - When enabled, identities can exchange a JWT token and a certificate signing request (CSR) \n        for a client certificate during enrollment. The certificate can then be used for standard certificate-based\n        authentication.\n\n- **enrollToTokenEnabled** - When enabled, identities can use a JWT token to enroll. The current token or future tokens\n        may be used for authentication.\n\n- **enrollNameClaimsSelector** - Specifies which JWT claim contains the identity name. Accepts a JSON pointer \n        (e.g., `/preferred_username`) or a simple property name (e.g., `preferred_username`, automatically converted to\n        `/preferred_username`). Defaults to `/sub` if not specified. The extracted value becomes the identity name in Ziti.\n\n- **enrollAttributeClaimsSelector** - Specifies which JWT claims to extract as identity attributes during enrollment.\n        Accepts a JSON pointer (e.g., `/roles`) or a simple property name (e.g., `roles`). Extracted attributes are \n        applied to the newly enrolled identity for use in authorization policies.\n\n- **enrollAuthPolicyId** - Specifies the authentication policy to apply to newly enrolled identities. This determines\n        what authentication methods are available for the identity post-enrollment.\n\nAdditionally the existing property named **claimsProperty** that specifies external id to match identities to:\n\n- now supports a JSON pointer (e.g., `/id`) or a simple property name (e.g., `id`)\n- is used to populate the `externalId` field of the identity\n\n### Enrollment Paths\n\n#### Certificate Enrollment (enrollToCertEnabled)\n\nWhen certificate enrollment is enabled, unauthenticated users can:\n\n1. Obtain a list of available IdPs from the public Edge Client API `GET /external-jwt-signers` endpoint, where \n   `enrollToCertEnabled` is set to `true`\n2. Obtain a JWT from the configured OIDC provider\n3. Generate a certificate signing request (CSR)\n4. Submit an enrollment request with the JWT and CSR\n5. Have their identity created in Ziti with attributes extracted from JWT claims\n6. Receive a signed client certificate for certificate-based authentication\n\n#### Token Enrollment (enrollToTokenEnabled)\n\nWhen token enrollment is enabled, unauthenticated users can:\n\n1. Obtain a list of available IdPs from the public Edge Client API `GET /external-jwt-signers` endpoint, where \n   `enrollToTokenEnabled` is set to `true`\n1. Obtain a JWT from the configured OIDC provider\n2. Submit an enrollment request with the JWT\n3. Have their identity created in Ziti with attributes extracted from JWT claims\n4. Receive a Ziti API token for token-based authentication\n\n### Edge Management API\n\nThe Edge Management API provides full CRUD operations for configuring external JWT signers:\n\n- `POST /external-jwt-signers` - Create a new external JWT signer with all configuration options\n- `GET /external-jwt-signers` - List all configured external JWT signers\n- `GET /external-jwt-signers/{id}` - Retrieve a specific signer configuration\n- `PUT /external-jwt-signers/{id}` - Update all fields of a signer\n- `PATCH /external-jwt-signers/{id}` - Partially update a signer\n- `DELETE /external-jwt-signers/{id}` - Delete a signer\n\n### Edge Client API\n\nThe Edge Client API exposes a reduced set of external JWT signer information for unauthenticated enrollment requests:\n\n- `GET /external-jwt-signers` - List available JWT signers with enrollment capabilities\n\nThe client API response includes the following fields for each signer:\n\n- `name` - Signer name\n- `externalAuthUrl` - URL where users obtain JWT tokens\n- `clientId` - OIDC client ID\n- `scopes` - Requested OIDC scopes\n- `openIdConfigurationUrl` - OIDC discovery endpoint\n- `audience` - Expected token audience\n- `targetToken` - Token type to use (ACCESS or ID)\n- **`enrollToCertEnabled`** - Flag indicating certificate enrollment is available\n- **`enrollToTokenEnabled`** - Flag indicating token enrollment is available\n\n### CLI Commands\n\n**Create an external JWT signer with enrollment options:**\n```\nziti edge controller create ext-jwt-signer <name> <issuer> \\\n  --jwks-endpoint <url> \\\n  --audience <audience> \\\n  --enroll-to-cert \\\n  --enroll-to-token=false \\\n  --enroll-name-claims-selector preferred_username \\\n  --enroll-attr-claims-selector roles \\\n  --enroll-auth-policy <policy-id-or-name>\n```\n\n**Update enrollment options on an existing signer:**\n```\nziti edge controller update ext-jwt-signer <name|id> \\\n  --enroll-to-cert \\\n  --enroll-auth-policy <policy-id-or-name>\n```\n\n**List external JWT signers:**\n```\nziti edge controller list ext-jwt-signers\n```\n\n## Clustering Performance Improvements\n\nIn previous releases, model updates were submitted to raft one at at time. This prevented \nraft from being efficient by allowing command batching. This release allows multiple \nmodel updates to be in-flight at the same time. \n\nNew Configuration Options\n\n1. Raft Apply Timeout (cluster.applyTimeout)\n\nLocation: Controller configuration file, under the cluster section\nType: Duration\nDefault: 5s\nDescription: Timeout for applying commands to the Raft distributed log. Commands that exceed this timeout will trigger adaptive rate limiter backoff.\n\nExample:\n```\ncluster:\n  applyTimeout: 10s\n```\n\n2. Cluster Rate Limiter Configuration (cluster.rateLimiter)\n\nA new adaptive rate limiter that controls the submission of commands to the Raft cluster. Unlike the existing command rate limiter, this specifically manages in-flight Raft operations with adaptive window sizing.\n\nConfiguration Structure:\n```\ncluster:\n  rateLimiter:\n    enabled: true\n    minSize: 5\n    maxSize: 250\n    timeout: 30s\n```\n\nSub-options:\n\n  - enabled (boolean)\n    - Default: true\n    - Description: Enable/disable adaptive rate limiting for Raft command submission\n  - minSize (integer)\n    - Default: 5\n    - Minimum: 1\n    - Description: Minimum window size for concurrent in-flight Raft operations\n  - maxSize (integer)\n    - Default: 250\n    - Description: Maximum window size for concurrent in-flight Raft operations. Must be >= minSize\n  - timeout (duration)\n    - Default: 30s\n    - Description: Time after which outstanding work is assumed to have failed if not marked completed\n\n3. Restart Self on Snapshot (cluster.restartSelfOnSnapshot)\n\nLocation: Controller configuration file, under cluster section\nType: Boolean\nDefault: false\nDescription: When true, the controller will automatically restart itself when restoring a snapshot to an initialized system. When false, the controller will exit with code 0, requiring external process management to restart it.\n\nExample:\n```\ncluster:\n    restartSelfOnSnapshot: true\n```\n\n### New Metrics\n\nThe adaptive rate limiter exposes three new metrics:\n\n  1. raft.rate_limiter.queue_size (gauge)\n    - Current number of operations queued/in-flight\n  2. raft.rate_limiter.work_timer (timer)\n    - Duration of rate-limited operations\n  3. raft.rate_limiter.window_size (gauge)\n    - Current adaptive window size\n\n## Rate Limiter Algorithm Improvements\n\nThe adaptive rate limiter tracker now uses a success rate metric to decide when to grow or shrink its\nconcurrency window, instead of using raw queue position. An exponentially decaying histogram tracks the\nratio of successes to backoffs. When the success rate exceeds a configurable threshold, the window is\ngrown by a multiplicative increase factor. When it falls below the threshold, the window is shrunk by a\nmultiplicative decrease factor. The check intervals for increase and decrease are independently configurable.\n\nThis approach produces smoother, more stable window adjustments under varying load, avoiding the\nover-aggressive shrinking that could occur when queue position alone was used as the signal.\n\nThis specific rate limiter implementation is used in three places:\n* **Controller TLS handshake rate limiting** - controls the rate of incoming TLS handshakes on the controller\n* **Raft command submission** - controls the rate of commands submitted to the Raft distributed log\n* **Router control channel rate limiting** - controls the rate of requests from the router to the controller\n\nNote: this work was done in advance of enabling the TLS handshake rate limiter by default. The TLS\nhandshake rate limiter is not yet enabled by default, but can be enabled via the `tls.rateLimiter`\nconfiguration section.\n\nNew configuration options (available under each `rateLimiter` section):\n\n  - successThreshold (float)\n    - Default: 0.9\n    - Description: Success rate threshold above which the window size will be increased and below which it will be decreased\n  - increaseFactor (float)\n    - Default: 1.02\n    - Description: Multiplier applied to the current window size when growing. Must be greater than 1\n  - decreaseFactor (float)\n    - Default: 0.9\n    - Description: Multiplier applied to the current window size when shrinking. Must be between 0 and 1\n  - increaseCheckInterval (integer)\n    - Default: 10\n    - Description: Number of successes between window size increase checks\n  - decreaseCheckInterval (integer)\n    - Default: 10\n    - Description: Number of backoffs between window size decrease checks\n\n## Background Processing for Identity Updates\n\nIdentity environment and authenticator updates that occur during authentication are now processed asynchronously in the background. \nThis prevents authentication requests from blocking when the system is under load, significantly improving resilience during thundering herd scenarios.\n\nWhen the background queue fills up, updates can be dropped as they will be refreshed on the next authentication attempt. \nThis allows the system to gracefully handle load spikes without impacting authentication performance.\n\nFor now, dropping entries when the queue fills will be disabled by default, but can be enabled, see below.\n\n### Configuration\n\nA new `command.background` configuration section controls the background processing behavior:\n\n```yaml\n  command:\n    background:\n      enabled: true           # Enable background processing (default: true)\n      queueSize: 1000        # Maximum queue size (default: 1000)\n      dropWhenFull: true     # Drop updates when queue is full (default: false)\n      delayThreshold: 50ms   # The threshold for how long updates are taking before starting to background updates\n```\n\nNote that the `commandRateLimiter` configuration section may instead be specified under `command` as `rateLimiter`.\n\nExample:\n\n```yaml\n  command:\n    background:\n      enabled: true\n      queueSize: 250\n      dropWhenFull: false\n      delayThreshold: 50ms\n    rateLimiter:\n      enabled:   true\n      maxQueued: 25\n```\n\nNote that if command rate limiter configuration is specified in both locations, the settings under `command` will take \nprecedence. The standalone `commandRateLimiter` section may be deprecated in the future.\n\n### Metrics\n\nWhen background processing is enabled, the following metrics are exposed:\n\n- command.background.queue_size - Current number of queued background tasks\n- command.background.worker_count - Current number of worker goroutines\n- command.background.busy_workers - Number of workers currently processing tasks\n- command.background.work_timer - Timer tracking background task execution (includes histogram, meter, and count)\n- command.background.dropped_entries - Count of dropped updates when queue is full (only when dropWhenFull is enabled)\n\n## New proxy.v1 Config Type\n\n*Originally released in 1.7.0*\n\nAdded support for dynamic service proxies with configurable binding and protocol options.\nThis allows Edge Routers and Tunnelers to create proxy endpoints that can forward traffic for Ziti services.\n\nThis differs from intercept.v1 in that intercept.v1 will intercept traffic on specified\nIP addresses or DNS entries to forward to a service using tproxy or tun interface,\ndepending on implementation.\n\nA proxy on the other hand will just start a regular TCP/UDP listener on the configured port,\nso traffic will have to be configured for that destination.\n\nExample proxy.v1 Configuration:\n\n```\n  {\n    \"port\": 8080,\n    \"protocols\": [\"tcp\"],\n    \"binding\": \"0.0.0.0\"\n  }\n```\n\nConfiguration Properties:\n  - port (required): Port number to listen on (1-65535)\n  - protocols (required): Array of supported protocols (tcp, udp)\n  - binding (optional): Interface to bind to. For the ER/T defaults to the configured lanIF config property.\n\nThis config type is currently supported by the ER/T when running in either proxy or tproxy mode.\n\n## Alert Events (BETA)\n\n*Originally released in 1.7.0*\n\nA new alert event type has been added to allow Ziti components to emit alerts for issues that network operators can address.\nAlert events are generated when components encounter problems such as service configuration errors or resource\navailability issues.\n\nAlert events include:\n  - Alert source type and ID (currently supports routers, with controller and SDK support planned for future releases)\n  - Severity level (currently supports error, with info and warning planned for future releases)\n  - Alert message and supporting details\n  - Related entities (router, identity, service, etc.) associated with the alert\n\nExample alert event when a router cannot bind a configured network interface:\n\n```\n  {\n    \"namespace\": \"alert\",\n    \"event_src_id\": \"ctrl1\",\n    \"timestamp\": \"2021-11-08T14:45:45.785561479-05:00\",\n    \"alert_source_type\": \"router\",\n    \"alert_source_id\": \"DJFljCCoLs\",\n    \"severity\": \"error\",\n    \"message\": \"error starting proxy listener for service 'test'\",\n    \"details\": [\n      \"unable to bind eth0, no address\"\n    ],\n    \"related_entities\": {\n      \"router\": \"DJFljCCoLs\",\n      \"identity\": \"DJFljCCoLs\",\n      \"service\": \"3DPjxybDvXlo878CB0X2Zs\"\n    }\n  }\n```\n\nAlert events can be consumed through the standard event system and logged to configured event handlers for monitoring and alerting purposes.\n\nThese events are currently in Beta, as the format is still subject to change. Once they've been in use in production for a while\nand proven useful, they will be marked as stable.\n\n## Azure Service Bus Event Sink\n\n*Originally released in 1.7.0. Contributed by @ffaraone.*\n\nAdds support for streaming controller events to Azure Service Bus.\nThe new logger enables real-time event streaming from the OpenZiti controller to Azure Service Bus\nqueues or topics, providing integration with Azure-based monitoring and analytics systems.\n\nTo enable the Azure Service Bus event logger, add configuration to the controller config file under the events section:\n\n```\n  events:\n    serviceBusLogger:\n      subscriptions:\n        - type: circuit\n        - type: session\n        - type: metrics\n          sourceFilter: .*\n          metricFilter: .*\n        # Add other event types as needed\n      handler:\n        type: servicebus\n        format: json\n        connectionString: \"Endpoint=sb://your-namespace.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=your-key\"\n        topic: \"ziti-events\"          # Use 'topic' for Service Bus topic\n        # queue: \"ziti-events-queue\"  # Or use 'queue' for Service Bus queue\n        bufferSize: 100                # Optional, defaults to 50\n```\n\n- Required configuration:\n    - format: Event format, currently supports only json\n    - connectionString: Azure Service Bus connection string\n    - Either topic or queue: Destination name (mutually exclusive)\n\n- Optional configuration:\n    - bufferSize: Internal message buffer size (default: 50)\n\n## OIDC is now enabled by default\n\nThe controller now automatically adds the `edge-oidc` API binding to any web listener that hosts\nthe `edge-client` API, even when `edge-oidc` is not explicitly listed in the `web` section of the\nconfiguration. This means OIDC-based authentication is available out of the box without requiring\nchanges to existing controller configurations.\n\n### Where OIDC binds\n\nThe `edge-oidc` binding is added to the same web listener(s) as `edge-client`. If `edge-client` is\nhosted on `127.0.0.1:1280`, the OIDC endpoints will be available on that same address under the\n`/oidc` path (e.g. `https://127.0.0.1:1280/oidc/.well-known/openid-configuration`).\n\nThe controller capabilities returned by `GET /version` will include `OIDC_AUTH` and the\n`edge-oidc` entry will be present in `apiVersions` when OIDC is active.\n\n### Opting out\n\nIf OIDC should not be enabled automatically, set `disableOidcAutoBinding: true` under `edge.api`:\n\n```yaml\nedge:\n  api:\n    address: 127.0.0.1:1280\n    sessionTimeout: 30m\n    disableOidcAutoBinding: true\n```\n\nWhen `disableOidcAutoBinding` is `true`, OIDC will only be active if `edge-oidc` is explicitly\nlisted as a binding in the `web` section. \n\nWhen OIDC is not enabled, all (SDK) clients will revert to using legacy authentication.\nLegacy authentication does not support multiple controllers or HA in general. Clients will treat\ntheir controller as if it is a single controller instance and will function as if no other controllers\nexist.\n\n\n## WWW-Authenticate Headers\n\nThe controller's Edge APIs now include `WWW-Authenticate` headers on `401 Unauthorized` responses,\nper issuer: https://github.com/openziti/ziti/issues/3356. These headers provide insight into why\na token was rejected. The main benefit of these headers is to convey information for JWT backed\nAPI Sessions and API Session authentication where a token may not have been provided (missing),\nis used beyond its expiration date (expired), or the token has become invalid for any other\nreason (invalid).\n\n`www-authenticate` headers are provided as a single header instance with multiple challenge values\nseparate by commas.\n\n### No Credentials Provided\n\nWhen a request hits a protected endpoint without any credentials, a single `WWW-Authenticate` header\nis returned listing both accepted auth schemes as comma-separated challenges:\n\n```\nWWW-Authenticate: zt-session realm=\"zt-session\" error=\"missing\" error_description=\"no matching token was provided\",Bearer realm=\"openziti-oidc\" error=\"missing\" error_description=\"no matching token was provided\"\n```\n\n### Token Errors\n\nWhen a token is present but cannot be accepted, the header identifies the scheme and what went wrong:\n\n```\nWWW-Authenticate: Bearer realm=\"openziti-oidc\" error=\"expired\" error_description=\"token expired\"\nWWW-Authenticate: Bearer realm=\"openziti-oidc\" error=\"invalid\" error_description=\"token is invalid\"\nWWW-Authenticate: zt-session realm=\"zt-session\" error=\"invalid\" error_description=\"token is invalid\"\n```\n\n### OIDC External JWT — Primary Authentication\n\nWhen an auth policy requires an external JWT signer for primary authentication (e.g., a PKCE flow\nbacked by an ext-jwt signer), the header identifies which signers are accepted and what went wrong.\nMultiple accepted signers are pipe-delimited in the `id` and `issuer` parameters:\n\n```\nWWW-Authenticate: Bearer realm=\"openziti-primary-ext-jwt\" error=\"missing\" error_description=\"no matching token was provided\" id=\"signer-id-1|signer-id-2\" issuer=\"https://issuer1.example.com|https://issuer2.example.com\"\n```\n\nThe `error` value follows the same `missing`/`expired`/`invalid` pattern as standard bearer token errors.\n\n### OIDC External JWT — Secondary / MFA Authentication\n\nWhen an auth policy requires an external JWT signer as a secondary factor (step-up after primary\nauth succeeds), the header identifies the single required signer. The `error` value follows the\nsame `missing`/`expired`/`invalid` pattern:\n\n```\nWWW-Authenticate: Bearer realm=\"openziti-secondary-ext-jwt\" error=\"missing\" error_description=\"no matching token was provided\" id=\"<signer-id>\" issuer=\"<issuer>\"\n```\n\n### Anonymous Endpoints\n\nUnauthenticated endpoints such as version information do not return `WWW-Authenticate` headers.\n\n## HA Preferred Leaders\n\nControllers can be marked as a preferred leader. \n\n**Example Config**\n```yaml\ncluster:\n  dataDir: /home/{{ .Model.MustVariable \"credentials.ssh.username\" }}/fablab/ctrldata\n  preferredLeader: true\n```\n\nIf a controller that is not marked preferredLeader becomes a preferredLeader, it will check \nif there's a node available that is marked as preferred. If there is one, or one later\njoins the cluster, the non-preferred node will attempt to transfer leadership to the \nnode that is marked as preferred.\n\n## Expanded Dynamic Cost Range\n\nThe dynamic cost range for smart routing has been expanded beyond the previous 64K limit. Under high\nload, terminators could saturate the cost space, making dynamic cost values meaningless for routing\ndecisions and leading to uneven load distribution. The expanded range allows for more granular cost\ndifferentiation even under heavy load.\n\n## Circuit ID and Error in Dial Failures\n\nDial failures now return the circuit ID and, when available, the error that caused the circuit to fail.\nPreviously, the circuit ID was only returned on successful dials. Note that SDKs will need to be\nupdated to surface the circuit id when a dial failure happens.\n\n## Multi-Underlay Control Channels\n\nRouter-to-controller control channels now support multiple underlays with priority-based message routing.\nThis allows time-sensitive control messages (heartbeats, routing, circuit requests) to be separated from\noperational data (metrics, inspections) across dedicated TCP connections, preventing bulk operations from\ndelaying user-affecting control plane traffic. This feature does not yet allow specifying multiple \nnetwork interfaces to use, to load balance data across.\n\n## Dialing Identity Forwarded to Hosting SDK\n\nThe identity ID and name of the dialing client are now forwarded to the hosting SDK when a circuit is\nestablished. This allows hosting applications to identify which identity initiated the connection,\nenabling identity-aware request handling on the server side. This will require SDK updates to add this\nto the API for hosting applications.\n\n## Controller-Initiated Control Channel Dials (BETA)\n\nControllers can now dial routers to establish control channels. Previously, routers were solely\nresponsible for dialing controllers. This feature is designed for deployments where one or more\ncontrollers are in a private network that routers cannot reach, but the controllers can dial out.\nA common scenario is an HA cluster where some controllers are publicly reachable and some are in\na private network. External routers connect to the public controllers normally, and the private\ncontrollers dial out to the routers.\n\n### Router Configuration\n\nRouters can configure one or more control channel listeners. Each listener specifies a bind address,\nan advertise address (reported to the controller), and optional groups for matching.\n\n```yaml\nctrl:\n  listeners:\n    - bind: tls://0.0.0.0:6262\n      advertise: tls://router.example.com:6262\n      groups:\n        - default\n```\n\nThe router is the authoritative source of ctrl channel listener information, similar to link\nlisteners. When a router connects to any controller, it reports its configured `ctrlChanListeners`\nand the controller data model is updated automatically. This means that in most deployments —\nwhere the router can reach at least one controller — no manual configuration of listener addresses\nis needed on the controller side.\n\nThe `ctrlChanListeners` field can also be set via the CLI for cases where a router cannot reach\nany controller and thus cannot initialize the data model itself:\n\n```bash\nziti edge update edge-router myRouter --bootstrap-ctrl-chan-listener 'tls://router.example.com:6262=group1,group2'\n```\n\nGroups default to `[\"default\"]` if not specified.\n\n### Controller Configuration\n\nThe controller dialer is disabled by default and must be explicitly enabled. When enabled, the\ncontroller will dial routers that have control channel listeners configured and are not already\nconnected.\n\n```yaml\nctrl:\n  dialer:\n    enabled: true\n    groups:\n      - default\n    dialDelay: 30s\n    minRetryInterval: 1s\n    maxRetryInterval: 5m\n    retryBackoffFactor: 1.5\n    fastFailureWindow: 5s\n    queueSize: 32\n    maxWorkers: 10\n```\n\n- `enabled` - Enables the controller dialer (default: `false`)\n- `groups` - List of groups to match against router listener groups (default: `[\"default\"]`)\n- `dialDelay` - Delay before the controller starts dialing after boot (default: `30s`)\n- `minRetryInterval` - Minimum backoff delay between dial retries (default: `1s`)\n- `maxRetryInterval` - Maximum backoff delay between dial retries (default: `5m`)\n- `retryBackoffFactor` - Multiplier applied to the retry delay on each failure, jittered +/- 0.5 (default: `1.5`)\n- `fastFailureWindow` - If a connection drops within this window after connecting, backoff continues rather than resetting (default: `5s`)\n- `queueSize` - Maximum number of pending dial jobs in the worker pool queue (default: `32`)\n- `maxWorkers` - Maximum number of concurrent dial workers (default: `10`)\n\nThe controller will only dial routers whose listener groups overlap with the controller's configured\ngroups. When a router advertises multiple ctrl channel listener addresses, the dialer rotates through\nthem on each failure so that an unreachable address does not block attempts to the others.\n\n### Metrics\n\nThe controller dialer worker pool exposes the following metrics under the `ctrl_channel.dialer` prefix:\n\n- `ctrl_channel.dialer.queue_size` - Current number of pending dial jobs in the queue\n- `ctrl_channel.dialer.worker_count` - Current number of dial worker goroutines\n- `ctrl_channel.dialer.busy_workers` - Number of workers currently executing a dial\n- `ctrl_channel.dialer.work_timer` - Timer tracking the duration of each dial attempt\n\n## SDK Inspection Support\n\nNew CLI commands have been added for inspecting SDK state, useful for diagnosing terminator and\nconnectivity issues.\n\n**Note:** SDK inspection requires SDK support. Currently only the Go SDK supports inspection,\nas of version 1.5.0. Other SDKs will need to add support before these commands can be used\nwith them.\n\n### `ziti fabric inspect sdk`\n\nRetrieves SDK context inspection data from identities connected to routers.\n\n```\nziti fabric inspect sdk <target-selector> <identity-id>\n```\n\nThe `<target-selector>` is a regex matching router IDs (use `.*` for all routers). The\n`<identity-id>` is the identity whose SDK context you want to inspect. The command returns\ndetailed state from the connected SDK instance, including active services, terminators, and\nconnection status.\n\n### `ziti agent tunnel dump-sdk`\n\nDumps SDK context information from a running `ziti tunnel` process via the IPC agent.\n\n```\nziti agent tunnel dump-sdk\n```\n\nReturns JSON-formatted inspection data for all SDK contexts registered in the tunnel process,\nincluding service listeners, connections, and terminator state.\n\n## Revocation System Improvements\n\nWhen a session is refreshed, the old refresh token's revocation is no longer created\nsynchronously through raft. Instead, revocations are queued in memory and flushed in\nbatches on a configurable interval. This removes the database and raft as a bottleneck\non token refreshes. If the old token is close to expiring, the revocation is skipped\nentirely.\n\nNew configuration tunables under `edge.oidc`:\n\n| Key | Default | Description |\n|-----|---------|-------------|\n| `revocationMinTokenLifetime` | unset | Skip revocation if the old token expires within this duration (must be < 50% of `refreshTokenDuration`) |\n| `revocationBucketInterval` | `1m` | Bucket window for batching revocations before flushing through raft |\n| `revocationBucketMaxSize` | `200` | Max revocations per raft entry |\n| `revocationMaxQueued` | `25000` | Max revocations queued in memory before dropping |\n| `revocationEnforcerFrequency` | `1m` | How often expired revocations are purged (leader only) |\n\n## CLI Reorganization Details\n\nThe CLI has been reorganized so that edge and fabric entity management commands are \navailable directly at the top level. The `ziti edge` and `ziti fabric` command trees \nremain fully functional — the new top-level commands are additional entry points, not \nreplacements.\n\n### Top-Level CRUD Commands\n\nEntity create, delete, list, and update operations that previously required the \n`ziti edge` or `ziti fabric` prefix are now available directly:\n\n| New command | Previous command |\n|---|---|\n| `ziti create identity ...` | `ziti edge create identity ...` |\n| `ziti delete service ...` | `ziti edge delete service ...` |\n| `ziti list edge-routers` | `ziti edge list edge-routers` |\n| `ziti update identity ...` | `ziti edge update identity ...` |\n| `ziti list circuits` | `ziti fabric list circuits` |\n| `ziti delete terminator ...` | `ziti fabric delete terminator ...` |\n\nAll edge and fabric entities are available under the consolidated commands. When an \nentity name exists in both edge and fabric (e.g., `service`, `router`), the edge \nversion is the default. Fabric equivalents are accessible with a `fabric-` prefix \n(e.g., `ziti list fabric-services`).\n\n### Top-Level Login\n\nSession management is now available at the top level:\n\n| New command | Previous command |\n|---|---|\n| `ziti login` | `ziti edge login` |\n| `ziti login forget` | `ziti edge login forget` |\n| `ziti login use` | `ziti edge use` |\n\n### Breaking Change: `ziti create ca`\n\n`ziti create ca` now creates a Ziti edge Certificate Authority, matching the \nbehavior of `ziti edge create ca`. Previously, `ziti create ca` was a PKI command \nfor generating CA certificates on the local filesystem.\n\nThe PKI command is still available at its original location:\n\n```\nziti pki create ca ...\n```\n\nScripts that use `ziti create ca` for PKI operations should be updated to use \n`ziti pki create ca` instead.\n\n## Multiple DNS Upstreams\n\nThe tunneler's DNS resolver now accepts multiple upstream DNS servers and fans out recursive queries\nto all of them in parallel, rather than being limited to a single upstream. This is useful for split-horizon\nsetups where different resolvers are authoritative for different zones, and for environments where a primary\nresolver may be slow or unreachable.\n\n### CLI\n\nThe `ziti tunnel --dnsUpstream` flag is now a repeatable string slice. Upstreams can be listed by repeating\nthe flag or by passing a comma-separated value:\n\n```bash\nziti tunnel run \\\n  --dnsUpstream udp://10.96.0.10:53 \\\n  --dnsUpstream tcp://8.8.8.8:53\n```\n\n### Router Config\n\nIn `xgress_edge_tunnel` router configs, `options.dnsUpstream` now accepts either a single string (as before)\nor a list of strings. Existing configs continue to work unchanged:\n\n```yaml\n# single upstream (unchanged)\noptions:\n  dnsUpstream: udp://10.96.0.10:53\n\n# multiple upstreams\noptions:\n  dnsUpstream:\n    - udp://10.96.0.10:53\n    - tcp://8.8.8.8:53\n```\n\n### How Resolution Works\n\nWhen a query comes in, the resolver dispatches it to every configured upstream concurrently. The first\nresponse with `RCODE=NOERROR` wins and is returned to the client immediately, so split-horizon lookups\nwork even if one upstream is slow. If no upstream returns NOERROR, the resolver picks the best-ranked\nnon-NOERROR reply (NXDOMAIN > SERVFAIL > REFUSED) so authoritative negative answers aren't masked by\ntransport failures. If every upstream fails to respond, the query is treated as unanswerable and handled\nper the configured `dnsUnanswerable` disposition.\n\n## Agent Inspect\n\nThe `ziti agent` CLI now has a generic `inspect` subcommand that works against any ziti process\n(controller, router, or tunneler) over the local IPC agent channel. It sends the inspect request\ndirectly to the target process, so unlike `ziti fabric inspect` it doesn't fan out through the\ncontroller and doesn't require network connectivity to the target.\n\n```\nziti agent inspect <value> [values...]\n```\n\nValues are matched against whatever the target process exposes. Common inspect keys:\n\n* Routers: `stackdump`, `links`, `config`, `metrics`, `sdk-terminators`, `ert-terminators`,\n  `router-circuits`, `router-data-model`, `router-controllers`\n* Controllers: `stackdump`, `config`, `metrics`, `connected-routers`, `connected-peers`,\n  `cluster-config`, `router-messaging`, `terminator-costs`, `data-model-index`\n* Tunnelers: `stackdump`, `sdk`\n\n## Current Beta Features\n\nBeta features are still under development and are subject to change. They should\nbe usable in their released form. Though unlikely, there is a small chance they will \nbe removed. \n\n* Basic Permission System\n* Alert Events\n* Controller-Initiated Control Channel Dials\n\n## Component Updates and Bug Fixes\n\n* github.com/openziti/agent: [v1.0.31 -> v1.0.33](https://github.com/openziti/agent/compare/v1.0.31...v1.0.33)\n* github.com/openziti/channel/v4: [v4.2.28 -> v4.3.11](https://github.com/openziti/channel/compare/v4.2.28...v4.3.11)\n    * [Issue #242](https://github.com/openziti/channel/issues/242) - Reconnecting channel shouldn't allow changing ids\n    * [Issue #235](https://github.com/openziti/channel/issues/235) - Bump allowed hello message headers size to 16k from 4k\n    * [Issue #228](https://github.com/openziti/channel/issues/228) - Ensure that Underlay never return nil on MultiChannel\n    * [Issue #226](https://github.com/openziti/channel/issues/226) - Allow specifying a minimum number of underlays for a channel, regardless of underlay type\n    * [Issue #225](https://github.com/openziti/channel/issues/225) - Add ChannelCreated to the UnderlayHandler API to allow handlers to be initialized with the channel before binding\n    * [Issue #224](https://github.com/openziti/channel/issues/224) - Update the underlay dispatcher to allow unknown underlay types to fall through to the default\n    * [Issue #222](https://github.com/openziti/channel/issues/222) - Allow injecting the underlay type into messages\n\n* github.com/openziti/edge-api: [v0.26.47 -> v0.28.1](https://github.com/openziti/edge-api/compare/v0.26.47...v0.28.1)\n    * [Issue #175](https://github.com/openziti/edge-api/issues/175) - ctrlChanListeners should have x-omit-empty: false attribute\n    * [Issue #170](https://github.com/openziti/edge-api/issues/170) - Add preferredLeader flag to controllers\n    * [Issue #167](https://github.com/openziti/edge-api/issues/167) - Add ctrlChanListeners to router types\n    * [Issue #164](https://github.com/openziti/edge-api/issues/164) - Add permissions list to identity\n\n* github.com/openziti/foundation/v2: [v2.0.72 -> v2.0.90](https://github.com/openziti/foundation/compare/v2.0.72...v2.0.90)\n    * [Issue #472](https://github.com/openziti/foundation/issues/472) - Add support for multi-bit set/get to AtomicBitSet\n    * [Issue #464](https://github.com/openziti/foundation/issues/464) - Add support for -pre in versions\n    * [Issue #455](https://github.com/openziti/foundation/issues/455) - Correctly close goroutine pool when external close is signaled\n    * [Issue #452](https://github.com/openziti/foundation/issues/452) - Goroutine pool with a min worker count of 1 can drop to 0 workers due to race condition\n\n* github.com/openziti/identity: [v1.0.111 -> v1.0.128](https://github.com/openziti/identity/compare/v1.0.111...v1.0.128)\n    * [Issue #68](https://github.com/openziti/identity/issues/68) - Shutdown file watcher when stopping identity watcher\n\n* github.com/openziti/metrics: [v1.4.2 -> v1.4.5](https://github.com/openziti/metrics/compare/v1.4.2...v1.4.5)\n    * [Issue #58](https://github.com/openziti/metrics/issues/58) - Add GaugeFloat64 support\n    * [Issue #56](https://github.com/openziti/metrics/issues/56) - underlying resources of reference counted meters are not cleaned up when reference count hits zero\n\n* github.com/openziti/runzmd: [v1.0.80 -> v1.0.90](https://github.com/openziti/runzmd/compare/v1.0.80...v1.0.90)\n* github.com/openziti/sdk-golang: [v1.2.3 -> v1.7.0](https://github.com/openziti/sdk-golang/compare/v1.2.3...v1.7.0)\n    * [Issue #901](https://github.com/openziti/sdk-golang/issues/901) - Move xgress back to having retransmitter goroutine per-xgress\n    * [Issue #906](https://github.com/openziti/sdk-golang/issues/906) - Fix potential nil references on session service structs\n    * [Issue #897](https://github.com/openziti/sdk-golang/issues/897) - Allow xgress to use pull model for reads when appropriate\n    * [Issue #895](https://github.com/openziti/sdk-golang/issues/895) - Limit effect sudden rtt spikes can have on rtt moving average\n    * [Issue #902](https://github.com/openziti/sdk-golang/issues/902) - Inspect response message content types are mixed up\n    * [Issue #887](https://github.com/openziti/sdk-golang/issues/887) - Fix listener manager cleanup\n    * [Issue #886](https://github.com/openziti/sdk-golang/issues/886) - When controller is busy during service refresh, backoff and retry instead of falling back to full refresh\n    * [Issue #885](https://github.com/openziti/sdk-golang/issues/885) - Only compare relevant service fields when looking for changes\n    * [Issue #884](https://github.com/openziti/sdk-golang/issues/884) - Add deadline for bind establishment\n    * [Issue #883](https://github.com/openziti/sdk-golang/issues/883) - Router level listener can be left open if multi-listener closes during listener establishment\n    * [Issue #877](https://github.com/openziti/sdk-golang/issues/877) - Handle differences in xgress eof/end-of-circuit handling by adding a capabilities exchange\n    * [Issue #832](https://github.com/openziti/sdk-golang/issues/832) - Fuzz session refresh timers\n    * [Issue #879](https://github.com/openziti/sdk-golang/issues/879) - Return the connId in inspect response\n    * [Issue #878](https://github.com/openziti/sdk-golang/issues/878) - Fix responses from rx goroutines\n    * [Issue #874](https://github.com/openziti/sdk-golang/issues/874) - Add inspect support at the context level\n    * [Issue #871](https://github.com/openziti/sdk-golang/issues/871) - Make SDK better at sticking to MaxTerminator terminators\n    * [Issue #708](https://github.com/openziti/sdk-golang/issues/708) - Support for Go's built-in context in Dial methods\n    * [Issue #860](https://github.com/openziti/sdk-golang/issues/860) - Make the dialing identity's id and name available on dialed connections\n    * [Issue #857](https://github.com/openziti/sdk-golang/issues/857) - Use new error code and retry hints to correctly react to terminator errors\n    * [Issue #847](https://github.com/openziti/sdk-golang/issues/847) - Ensure the initial version check succeeds, to ensure we don't legacy sessions on ha or oidc-enabled controllers\n    * [Issue #824](https://github.com/openziti/sdk-golang/pull/824) - release notes and hard errors on no TOTP handler breaks partial auth events\n    * [Issue #818](https://github.com/openziti/sdk-golang/issues/818) - Full re-auth should not clear services list, as that breaks the on-change logic\n    * [Issue #817](https://github.com/openziti/sdk-golang/issues/817) - goroutines can get stuck when iterating over randomized HA controller list\n    * [Issue #736](https://github.com/openziti/sdk-golang/issues/736) - Migrate from github.com/mailru/easyjson\n    * [Issue #813](https://github.com/openziti/sdk-golang/issues/813) - SDK doesn't stop close listener when it detects that a service being hosted gets deleted\n    * [Issue #811](https://github.com/openziti/sdk-golang/issues/811) - Credentials are lost when explicitly set\n    * [Issue #807](https://github.com/openziti/sdk-golang/issues/807) - Don't send close from rxer to avoid blocking\n    * [Issue #800](https://github.com/openziti/sdk-golang/issues/800) - Tidy create service session logging\n\n* github.com/openziti/secretstream: [v0.1.39 -> v0.1.49](https://github.com/openziti/secretstream/compare/v0.1.39...v0.1.49)\n* github.com/openziti/transport/v2: [v2.0.188 -> v2.0.215](https://github.com/openziti/transport/compare/v2.0.188...v2.0.215)\n    * [Issue #31](https://github.com/openziti/transport/issues/31) - ipv6 Transport Address Parsing\n    * [Issue #149](https://github.com/openziti/transport/issues/149) - Archive transwarp code\n\n* github.com/openziti/xweb/v3: [v2.3.4 -> v3.0.4](https://github.com/openziti/xweb/compare/v2.3.4...v3.0.4)\n    * [Issue #32](https://github.com/openziti/xweb/issues/32) - watched identities sometimes don't reload when changed\n\n* github.com/openziti/go-term-markdown: v1.0.1 (new)\n* github.com/openziti/ziti/v2: [v1.6.8 -> v2.0.0](https://github.com/openziti/ziti/compare/v1.6.8...v2.0.0)\n    * [Issue #3855](https://github.com/openziti/ziti/issues/3855) - Filter current api session certs list by current api session\n    * [Issue #3838](https://github.com/openziti/ziti/issues/3838) - List controllers on management API has incorrect permissions check\n    * [Issue #3837](https://github.com/openziti/ziti/issues/3837) - Create db snapshot with path has incorrect permissions check\n    * [Issue #3846](https://github.com/openziti/ziti/issues/3846) - OIDC token binds client cert during non-cert auth, causes PoP failures\n    * [Issue #3809](https://github.com/openziti/ziti/issues/3809) - Support CSR submission during OIDC authentication for session-bound certificates\n    * [Issue #3830](https://github.com/openziti/ziti/issues/3830) - statemanager is holding on to edge connections and they're never getting cleared\n    * [Issue #3824](https://github.com/openziti/ziti/issues/3824) - Allow calling inspect using the IPC agent on the controller, router and go tunnel\n    * [Issue #2049](https://github.com/openziti/ziti/issues/2049) - The ziti agent command should have a controller connection status\n    * [Issue #3784](https://github.com/openziti/ziti/issues/3784) - Fix link registry race condition on reporting links on reconnect\n    * [Issue #3734](https://github.com/openziti/ziti/issues/3734) - Enforce client certificate proof-of-possession on controller REST API for OIDC sessions\n    * [Issue #3806](https://github.com/openziti/ziti/issues/3806) - Expose OpenZiti-specific login and MFA endpoints in the OIDC discovery document\n    * [Issue #3818](https://github.com/openziti/ziti/issues/3818) - Filtering policies by keywords `Dial` and `Bind` doesn't work\n    * [Issue #3816](https://github.com/openziti/ziti/issues/3816) - Support multiple upstream DNS providers in ziti tunnel and ER/T\n    * [Issue #3699](https://github.com/openziti/ziti/issues/3699) - Consolidate CLI edge and fabric commands in top level create/update/delete/list/login commands\n    * [Issue #3788](https://github.com/openziti/ziti/issues/3788) - OIDC Endpoints return 400 Bad Request instead of underlying error\n    * [Issue #3680](https://github.com/openziti/ziti/issues/3680) - adds revocation control to CLI and Management API\n    * [Issue #3717](https://github.com/openziti/ziti/issues/3717) - Generic error message for specific error\n    * [Issue #3543](https://github.com/openziti/ziti/issues/3543) - New Circuit Failure code for sockets not available\n    * [Issue #3364](https://github.com/openziti/ziti/issues/3364) - Make no such host error specific\n    * [Issue #2888](https://github.com/openziti/ziti/issues/2888) - New specific Error code for port not allowed\n    * [Issue #2859](https://github.com/openziti/ziti/issues/2859) - Create specific error code for DNS failed resolution\n    * [Issue #1580](https://github.com/openziti/ziti/issues/1580) - Invalid link destination should have a specific error code\n    * [Issue #3706](https://github.com/openziti/ziti/issues/3706) - Increase link payload/ack queue sizes and make them configurable\n    * [Issue #3778](https://github.com/openziti/ziti/issues/3778) - SetRouterDataModel can deadlock in the router\n    * [Issue #3777](https://github.com/openziti/ziti/issues/3777) - With the new circuit reserve, we can have circuits with no path in the controller circuit set, which can cause panics\n    * [Issue #3770](https://github.com/openziti/ziti/issues/3770) - Update Token Requests Should Close Channel Connections If Invalid\n    * [Issue #3762](https://github.com/openziti/ziti/issues/3762) - Revocations not included in full router data model state\n    * [Issue #3757](https://github.com/openziti/ziti/issues/3757) - Mesh peer signing cert from header is overwritten by TLS underlay cert\n    * [Issue #3756](https://github.com/openziti/ziti/issues/3756) - TLS handshake rate limiter timeout check reads from wrong config scope\n    * [Issue #3755](https://github.com/openziti/ziti/issues/3755) - commandHandler config read from wrong scope\n    * [Issue #3754](https://github.com/openziti/ziti/issues/3754) - dialFailed drops applyFailed parameter, preventing duplicate link retry jitter\n    * [Issue #3753](https://github.com/openziti/ziti/issues/3753) - SPIFFE trust domain prefix check has swapped HasPrefix arguments\n    * [Issue #3746](https://github.com/openziti/ziti/issues/3746) - The controller connect events control channel handler leaks a goroutine\n    * [Issue #3747](https://github.com/openziti/ziti/issues/3747) - Update controller peer error marshalling for app code changes\n    * [Issue #3721](https://github.com/openziti/ziti/issues/3721) - Add CreateCircuitV3 to controller\n    * [Issue #3719](https://github.com/openziti/ziti/issues/3719) - Allow binding specific inspects to pass through to xgress listener implementations\n    * [Issue #3696](https://github.com/openziti/ziti/issues/3696) - oidc provider is non-deterministic for wildcard certs\n    * [Issue #3681](https://github.com/openziti/ziti/issues/3681) - coalesce OIDC JWT revocations to reduce controller write pressure\n    * [Issue #3683](https://github.com/openziti/ziti/issues/3683) - add fablab test for testing flow control changes over a longer term\n    * [Issue #3673](https://github.com/openziti/ziti/issues/3673) - revocation build-up in db and rdm\n    * [Issue #3674](https://github.com/openziti/ziti/issues/3674) - Update to Go 1.26\n    * [Issue #3496](https://github.com/openziti/ziti/issues/3496) - MFA TOTP Enrollment During OIDC Authentication Does Not Work\n    * [Issue #3609](https://github.com/openziti/ziti/issues/3609) - Stabilize terminator creation test for 2.0\n    * [Issue #3648](https://github.com/openziti/ziti/issues/3648) - tunnel: myCopy logs router ID as circuitId, causing misleading debug output\n    * [Issue #3658](https://github.com/openziti/ziti/issues/3658) - Raft cluster join fails with \"hello message too big\" when using long hostnames\n    * [Issue #3626](https://github.com/openziti/ziti/issues/3626) - controller: overlay bind point produces malformed URL in /versions apiBaseUrls\n    * [Issue #3635](https://github.com/openziti/ziti/issues/3635) - Allow controllers to dial routers to support more topologies\n    * [Issue #3607](https://github.com/openziti/ziti/issues/3607) - linux installer not upgradable from v1\n    * [Issue #3650](https://github.com/openziti/ziti/issues/3650) - Reroute doesn't proactively clean up orphaned route entries\n    * [Issue #3571](https://github.com/openziti/ziti/issues/3571) - Ensure 2.0 backwards compatibility with 1.6 and 1.5 using the smoketest\n    * [Issue #3636](https://github.com/openziti/ziti/issues/3636) - Adaptive rate limiter should use success rate rather than queue position\n    * [Issue #3600](https://github.com/openziti/ziti/issues/3600) - Add a preferredLeader flag, allow selected nodes to be preferred for raft leader, if they're available\n    * [Issue #3642](https://github.com/openziti/ziti/issues/3642) - Use xgress_common.Connection type for xgress_transport and xgress_proxy\n    * [Issue #3597](https://github.com/openziti/ziti/issues/3597) - Enable OIDC API by default\n    * [Issue #3624](https://github.com/openziti/ziti/issues/3624) - Multi-underlay control channel doesn't correctly handle lack of group secret on non-grouped underlays\n    * [Issue #3613](https://github.com/openziti/ziti/issues/3613) - Initializing cluster from existing db using `db:` config settings results in panic\n    * [Issue #3620](https://github.com/openziti/ziti/issues/3620) - Controller control channel heartbeats config parsing was erroneously nested under options parsing\n    * [Issue #3619](https://github.com/openziti/ziti/issues/3619) - Router connect events full sync interval was using min/max values meant for the batch interval\n    * [Issue #3618](https://github.com/openziti/ziti/issues/3618) - Router interfaceDiscovery.minReportInterval value was being set to checkInterval\n    * [Issue #3617](https://github.com/openziti/ziti/issues/3617) - Transit router disabled flag not passed through raft command structure\n    * [Issue #3333](https://github.com/openziti/ziti/issues/3333) - Updb user-lockout triggered by successful login attempts\n    * [Issue #3599](https://github.com/openziti/ziti/issues/3599) - Add gap detection and handling to router data model\n    * [Issue #3356](https://github.com/openziti/ziti/issues/3356) - Add WWW-Authenticate Headers\n    * [Issue #3074](https://github.com/openziti/ziti/issues/3074) - Dynamic cost range is too limited\n    * [Issue #3558](https://github.com/openziti/ziti/issues/3558) - terminator cost increased on egress dial success, not on circuit completion\n    * [Issue #3556](https://github.com/openziti/ziti/issues/3556) - global circuit costs not cleared when terminator is deleted\n    * [Issue #3557](https://github.com/openziti/ziti/issues/3557) - costing calculation for the weighted terminator selection strategy  is incorrect\n    * [Issue #2512](https://github.com/openziti/ziti/issues/2512) - Return circuit ID and error in dial failures\n    * [Issue #3569](https://github.com/openziti/ziti/issues/3569) - Version 2.0+ routers should not connect to controllers which do not support JWT formatted legacy sessions\n    * [Issue #3565](https://github.com/openziti/ziti/issues/3565) - Link dialer save 'is first conn' true, so all dials claim to be first, causing potential race condition\n    * [Issue #3575](https://github.com/openziti/ziti/issues/3575) - OIDC token endpoint code bugs possibly resulting in panics/eof errors\n    * [Issue #3550](https://github.com/openziti/ziti/issues/3550) - Support multi-underlay control channels\n    * [Issue #3535](https://github.com/openziti/ziti/issues/3535) - Remove the legacy xgress_edge_tunnel implementation\n    * [Issue #3547](https://github.com/openziti/ziti/issues/3547) - Add support for sending the dialing identity id and name to the hosting sdk\n    * [Issue #3541](https://github.com/openziti/ziti/issues/3541) - Remove option to disable the router data model in the controller\n    * [Issue #3540](https://github.com/openziti/ziti/issues/3540) - Handle UDP difference between proxy and tproxy implementations\n    * [Issue #3527](https://github.com/openziti/ziti/issues/3527) - ER/T UDP tunnels keep closed connections for 30s, preventing potential new good connections in that time\n    * [Issue #3526](https://github.com/openziti/ziti/issues/3526) - ER/T half-close logic is incorrect\n    * [Issue #3524](https://github.com/openziti/ziti/issues/3524) - Provide more error context to SDKs for terminator errors\n    * [Issue #3509](https://github.com/openziti/ziti/issues/3509) - Enforce policy on the router for oidc sessions, by closing open circuits and terminators when service access is lost\n    * [Issue #3531](https://github.com/openziti/ziti/issues/3531) - Remove created/updated/deleted terminator events. Obsoleted by entity change events.\n    * [Issue #3532](https://github.com/openziti/ziti/issues/3532) - Removed deprecated create identity <type> subcommands\n    * [Issue #3521](https://github.com/openziti/ziti/issues/3521) - Cleanup CLI to remove calls to os.Exit to be embed friendlier\n    * [Issue #3516](https://github.com/openziti/ziti/issues/3516) - Remove support for create terminator v1\n    * [Issue #3512](https://github.com/openziti/ziti/issues/3512) - Remove legacy link management code from the controller\n    * [Issue #3511](https://github.com/openziti/ziti/issues/3511) - router proxy mode fails to resolve interface if binding is 0.0.0.0\n    * [Issue #3503](https://github.com/openziti/ziti/issues/3503) - Allow routers to request current cluster membership information\n    * [Issue #3501](https://github.com/openziti/ziti/issues/3501) - Get cluster membership information from raft directly, rather than trying to cache it in the DB\n    * [Issue #3500](https://github.com/openziti/ziti/issues/3500) - Set a router data model timeline when initializing a new HA setup, rather than letting it stay blank\n    * [Issue #3504](https://github.com/openziti/ziti/issues/3504) - Reduce router data model full state updates\n    * [Issue #3492](https://github.com/openziti/ziti/pull/3492) - Bump openziti/ziti-console-assets from 3.12.9 to 4.0.0 in /dist/docker-images/ziti-controller in the all group\n    * [Issue #3484](https://github.com/openziti/ziti/issues/3484) - router ctrl channel handler for handling cluster changes has an initialization race condition\n    * [Issue #3477](https://github.com/openziti/ziti/issues/3477) - Optionally enable model changes triggered by login to be non-blocking and to be droppable if the system is under load\n    * [Issue #3473](https://github.com/openziti/ziti/issues/3473) - Enable tls handshake rate limiter by default and tweak default values.\n    * [Issue #3471](https://github.com/openziti/ziti/issues/3471) - Go tunneler is ignoring host config MaxConnections\n    * [Issue #3469](https://github.com/openziti/ziti/issues/3469) - Only send model updates on resubscribe if the RDM index has advanced\n    * [Issue #2573](https://github.com/openziti/ziti/issues/2573) - An edge router in a tight restart loop causes a resource leak on routers to which it connects.\n    * [Issue #3430](https://github.com/openziti/ziti/issues/3430) - Add permissions list to identity\n    * [Issue #2109](https://github.com/openziti/ziti/issues/2109) - Add Edge Management Read Only Capability\n    * [Issue #3435](https://github.com/openziti/ziti/issues/3435) - Add edge management API permissions by entity type and action\n    * [Issue #3441](https://github.com/openziti/ziti/issues/3441) - Update router connection tracker to interrogate active connections\n    * [Issue #3451](https://github.com/openziti/ziti/issues/3451) - ci - compare only stable releases when promoting\n    * [Issue #3437](https://github.com/openziti/ziti/issues/3437) - SDK OIDC token updates to routers should return an error if invalid\n    * [Issue #3348](https://github.com/openziti/ziti/issues/3348) - Unable to clear/reset the \"tags\" property on an entity to an empty object\n    * [Issue #3452](https://github.com/openziti/ziti/issues/3452) - `ziti agent cluster add` has bad behavior if the add address doesn't match the advertise address\n    * [Issue #3410](https://github.com/openziti/ziti/issues/3410) - Consolidate fabric REST API code with edge management and edge client code\n    * [Issue #3425](https://github.com/openziti/ziti/issues/3425) - RDM not properly responding to tunneler enabled flag changes\n    * [Issue #3420](https://github.com/openziti/ziti/issues/3420) - The terminator id cache uses the same id for all terminators in a host.v2 config, resulting in a single terminator\n    * [Issue #3419](https://github.com/openziti/ziti/issues/3419) - When using the router data model, precedence specified on the per-service identity mapping are incorrectly interpreted\n    * [Issue #3318](https://github.com/openziti/ziti/issues/3318) - Terminator creation seems to slow exponentially as the number of terminators rises from 10k to 20k to 40k\n    * [Issue #3407](https://github.com/openziti/ziti/issues/3407) - The CLI doesn't properly pass JWT authentication information to websocket endpoints\n    * [Issue #3359](https://github.com/openziti/ziti/issues/3359) - Ensure router data model subscriptions have reasonable performance and will scale\n    * [Issue #3354](https://github.com/openziti/ziti/issues/3354) - SDK/ENV Info from SDKs is not distributed in HA\n    * [Issue #3381](https://github.com/openziti/ziti/issues/3381) - the fabric service REST apis are missing the maxIdleTime property\n    * [Issue #3382](https://github.com/openziti/ziti/issues/3382) - Legacy service sessions generated pre-1.7.x are incompatible with v1.7.+ and need to be cleared\n    * [Issue #3339](https://github.com/openziti/ziti/issues/3339) - get router ctrl.endpoint from ctrls claim in JWT\n    * [Issue #3378](https://github.com/openziti/ziti/issues/3378) - login with file stopped working\n    * [Issue #3349](https://github.com/openziti/ziti/issues/3349) - UPDB OIDC login returns wrong content type\n    * [Issue #2324](https://github.com/openziti/ziti/issues/2324) - Add Ext-JWT/OIDC enrollment\n    * [Issue #3346](https://github.com/openziti/ziti/issues/3346) - Fix confusing attempt logging\n    * [Issue #3337](https://github.com/openziti/ziti/issues/3337) - Router reports \"no xgress edge forwarder for circuit\"\n    * [Issue #3345](https://github.com/openziti/ziti/issues/3345) - Clean up connect events tests and remove global XG registry\n    * [Issue #3264](https://github.com/openziti/ziti/issues/3264) - Allow routers to generate alert events in cases of service misconfiguration\n    * [Issue #3321](https://github.com/openziti/ziti/issues/3321) - Health Check API missing base path on discovery endpoint\n    * [Issue #3323](https://github.com/openziti/ziti/issues/3323) - router/tunnel static services fail to bind unless new param protocol is defined\n    * [Issue #3309](https://github.com/openziti/ziti/issues/3309) - Detect link connections meant for another router\n    * [Issue #3286](https://github.com/openziti/ziti/issues/3286) - edge-api binding doesn't have the correct path on discovery endpoints\n    * [Issue #3297](https://github.com/openziti/ziti/issues/3297) - stop promoting hotfixes downstream\n    * [Issue #3295](https://github.com/openziti/ziti/issues/3295) - make ziti tunnel service:port pairs optional\n    * [Issue #3291](https://github.com/openziti/ziti/issues/3291) - replace decommissioned bitnami/kubectl\n    * [Issue #3277](https://github.com/openziti/ziti/issues/3277) - Router can deadlock on closing a connection if the incoming data channel is full\n    * [Issue #3269](https://github.com/openziti/ziti/issues/3269) - Add host-interfaces config type\n    * [Issue #3258](https://github.com/openziti/ziti/issues/3258) - Add config type proxy.v1 so proxies can be defined dynamically for the ER/T\n    * [Issue #3259](https://github.com/openziti/ziti/issues/3259) - Interfaces config type not added due to wrong name\n    * [Issue #3265](https://github.com/openziti/ziti/issues/3265) - Forwarding errors should log at debug, since they are usual part of circuit teardown\n    * [Issue #3261](https://github.com/openziti/ziti/issues/3261) - ER/T dialed xgress connections may only half-close when peer is fully closed\n\n\n","reactions":{"url":"https://api.github.com/repos/openziti/ziti/releases/319661925/reactions","total_count":1,"+1":0,"-1":0,"laugh":0,"hooray":1,"confused":0,"heart":0,"rocket":0,"eyes":0},"mentions_count":3},{"url":"https://api.github.com/repos/openziti/ziti/releases/314229788","assets_url":"https://api.github.com/repos/openziti/ziti/releases/314229788/assets","upload_url":"https://uploads.github.com/repos/openziti/ziti/releases/314229788/assets{?name,label}","html_url":"https://github.com/openziti/ziti/releases/tag/v2.0.0-pre12","id":314229788,"author":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"node_id":"RE_kwDODVFMN84SusQc","tag_name":"v2.0.0-pre12","target_commitish":"main","name":"v2.0.0-pre12","draft":false,"immutable":false,"prerelease":true,"created_at":"2026-04-27T19:23:52Z","updated_at":"2026-04-27T19:31:15Z","published_at":"2026-04-27T19:31:15Z","assets":[{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/406821779","id":406821779,"node_id":"RA_kwDODVFMN84YP5uT","name":"checksums.sha256.txt","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"text/plain; charset=utf-8","state":"uploaded","size":798,"digest":"sha256:5537d2262b8715a991bad7f1e6f0953ccc5c6f359839763f432c680fc9fb1bd4","download_count":6,"created_at":"2026-04-27T19:31:13Z","updated_at":"2026-04-27T19:31:14Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre12/checksums.sha256.txt"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/406821777","id":406821777,"node_id":"RA_kwDODVFMN84YP5uR","name":"sbom-v2.0.0-pre12.spdx.json","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/json","state":"uploaded","size":982833,"digest":"sha256:8acd7b90622b4bea7eb46e685b822a78393bbfd8176e6de31b564e77052ce847","download_count":4,"created_at":"2026-04-27T19:31:13Z","updated_at":"2026-04-27T19:31:13Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre12/sbom-v2.0.0-pre12.spdx.json"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/406821776","id":406821776,"node_id":"RA_kwDODVFMN84YP5uQ","name":"source-v2.0.0-pre12.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":3976125,"digest":"sha256:2978fcef5f4f40ee14046d08fd60041f0f251f7373d2c3038b08b0dea90b3af4","download_count":6,"created_at":"2026-04-27T19:31:13Z","updated_at":"2026-04-27T19:31:13Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre12/source-v2.0.0-pre12.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/406821759","id":406821759,"node_id":"RA_kwDODVFMN84YP5t_","name":"ziti-darwin-amd64-2.0.0-pre12.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":54541463,"digest":"sha256:9b1710bf64b5035435c3f347980e7230d5b84babc87d573734b1a0f79ae3fa6c","download_count":9,"created_at":"2026-04-27T19:31:11Z","updated_at":"2026-04-27T19:31:13Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre12/ziti-darwin-amd64-2.0.0-pre12.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/406821755","id":406821755,"node_id":"RA_kwDODVFMN84YP5t7","name":"ziti-darwin-arm64-2.0.0-pre12.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":50867285,"digest":"sha256:f812ef353b5edcb6f9fdab36051791af62dd53438ae6ec8e19974615e1b1671d","download_count":8,"created_at":"2026-04-27T19:31:11Z","updated_at":"2026-04-27T19:31:13Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre12/ziti-darwin-arm64-2.0.0-pre12.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/406821757","id":406821757,"node_id":"RA_kwDODVFMN84YP5t9","name":"ziti-linux-amd64-2.0.0-pre12.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":55882606,"digest":"sha256:715b11bcae7faeca74b86f12fa2af0672292111aba07abfcb866f8e3c145296e","download_count":64,"created_at":"2026-04-27T19:31:11Z","updated_at":"2026-04-27T19:31:14Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre12/ziti-linux-amd64-2.0.0-pre12.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/406821756","id":406821756,"node_id":"RA_kwDODVFMN84YP5t8","name":"ziti-linux-arm-2.0.0-pre12.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":52330035,"digest":"sha256:994fec501b90b226cbd01bc981bcec00d769f096fa4ce66f1b218233f6ea744b","download_count":9,"created_at":"2026-04-27T19:31:11Z","updated_at":"2026-04-27T19:31:13Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre12/ziti-linux-arm-2.0.0-pre12.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/406821758","id":406821758,"node_id":"RA_kwDODVFMN84YP5t-","name":"ziti-linux-arm64-2.0.0-pre12.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":52343895,"digest":"sha256:34f83571a65af061a9a8afaff4e14d596cb9d87959f53042559898fe5da38938","download_count":11,"created_at":"2026-04-27T19:31:11Z","updated_at":"2026-04-27T19:31:13Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre12/ziti-linux-arm64-2.0.0-pre12.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/406821774","id":406821774,"node_id":"RA_kwDODVFMN84YP5uO","name":"ziti-windows-amd64-2.0.0-pre12.zip","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/zip","state":"uploaded","size":45163829,"digest":"sha256:2d342dd52a17efdc491fb6e411156119f5610a9b6f157ba951ec37e964e98801","download_count":14,"created_at":"2026-04-27T19:31:13Z","updated_at":"2026-04-27T19:31:15Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre12/ziti-windows-amd64-2.0.0-pre12.zip"}],"tarball_url":"https://api.github.com/repos/openziti/ziti/tarball/v2.0.0-pre12","zipball_url":"https://api.github.com/repos/openziti/ziti/zipball/v2.0.0-pre12","body":"# Release 2.0.0\n\n## What's New\n\nThis is the next major version release of OpenZiti, following the 1.0 release in April 2024.\nOf particular note is that HA controllers are now considered ready for general use.\nThis release also introduces a new permissions model, OIDC/JWT token-based enrollment, \nclustering performance improvements, and a number of other features and fixes. Because \nsome of these changes are not backwards compatible with older routers, we're marking this \nas a major version bump.\n\n### HA Controllers are now considered ready for general use\n\nThis is a pretty big milestone and marks the completion of work that's been ongoing for a couple of years.\nThe HA work has brought with it some notable changes. Authentication now uses JWTs by default. Using JWTs\nmeans the controllers don't need to store session and propagate them to the routers. This removes a bottleneck\nfrom the network and allows the load to be more easily distributed among controllers and routers.\n\nTo support distributed authentication, the routers now get a bespoke version of the data model. In addition\nto enabling distributed authentication this will allow us to remove the need for service polling and further \nreduce the load on controllers in the future.\n\n### Router Compatibility\n\nRelated to the JWT work, routers with version 2.+ will only work with controllers that are version 2.+. This means\nto upgrade your network, controllers should be upgraded first. Routers can then be upgraded individually.\n\n2.x routers should still work fine with older router versions.\n\nWe try very hard to avoid breaking changes like this, but sometimes the engineering trade-offs lead there. This change\nwas first made in the 1.7 release. That release has not been marked stable, and we have no plans to do so, because\nof the backwards incompatibility. \n\nIf you need to support in the 1.6.12+ range, see the section \"OIDC enabled by default\".\n\n### New Permissions Model (BETA)\n\nAs one feature goes out of beta, another arrives into beta. This release introduces a new permissions system\nfor more fine grained control to the management API. It's not expected to change, but may do so based on feedback\nfrom users.\n\n### CLI Reorganization\n\nThe `ziti` CLI has been reorganized to consolidate commands that were previously \nspread across `ziti edge` and `ziti fabric` into unified top-level commands. Entity \nmanagement is now available directly via `ziti create`, `ziti delete`, `ziti list`, \nand `ziti update`. Session management has been simplified with top-level `ziti login`. \nThe existing `ziti edge` and `ziti fabric` command trees remain available.\n\n**Breaking change:** `ziti create ca` now creates a Ziti edge Certificate Authority \n(previously it created a PKI CA). PKI CA creation is still available via `ziti pki create ca`.\n\nSee [CLI Reorganization Details](#cli-reorganization-details) for the full command mapping.\n\n### Updated Release Process\n\nWe have moved to creating `-preN` releases for major and minor versions. This way we can put out release candidates,\nor feature previews and put them through internal testing and let interested folks from the community try them out.\nThen, when we're ready, we can run the full validation suite against the last pre-release and retag it. \n\nPatch releases won't have `-preN` and should contain only high priority bug fixes.\n\n### Deprecation Cleanup\n\nSince we already have a breaking change, we're removing some other backwards compatibility code.\n\n* Controller managed links \n    * Router managed links were introduced in v0.30.0. \n    * If you're upgrading from an older versions, you'll want to upgrade to the latest 1.x release before jumping to 2.x\n    * Github tracking issue: https://github.com/openziti/ziti/issues/3512\n* `ziti edge create identity <type>`\n    * Identity types other than router were removed in v0.30.2\n    * The `type` can be dropped from the CLI command\n    * Github tracking issue: https://github.com/openziti/ziti/issues/3532\n* Terminator create/update/delete events\n    * These have been superseded by entity change events, which also have create/update/delete events for terminators\n    * Entity change events were introduced in v0.28.0\n    * Github tracking issue: https://github.com/openziti/ziti/issues/3531\n* `xgress_edge_tunnel` v1\n    * This is the first implementation of the tunneler in edge-router code (ER/T) which used legacy api sessions and services\n    * The v2 version uses the router data model and was introduced in v0.30.x\n    * Github tracking issue: https://github.com/openziti/ziti/issues/3516\n* Service policy filter `type = 1` / `type = 2`\n    * Service policy list queries now expect the string form (`type = \"Dial\"`, `type = \"Bind\"`) matching the REST API\n    * The integer form was an undocumented side effect of the internal storage format and never worked with the documented filter names\n    * Github tracking issue: https://github.com/openziti/ziti/issues/3818\n\n### Legacy Session Deprecation\n\nOIDC sessions are now preferred. They are the default, or will become the default for SDKs and tunnelers. They are also required\nwhen running HA. Legacy API and service session are now deprecated and will be removed in the OpenZiti v3.0.0 release. \n\n### Additional Features\n\n* Controllers can now optionally bind APIs using an OpenZiti identity\n* `ziti edge login` now supports the `--network-identity` flag to authenticate and establish connections through the Ziti overlay network\n* `ziti edge login` now supports using a bearer token with `--token` for authentication. The token is expected to be\n  provided as just the JWT, not with the \"Bearer \" prefix\n* Identity configuration can now be loaded from files or environment variables for flexible deployment scenarios\n* Identities can now be provisioned just-in-time through OIDC/JWT token-based enrollment\n* Multiple model updates can now be in-flight at the same time, improving clustering performance\n* Authentication-related model updates can now be non-blocking and even dropped if the system is too busy\n* Routers now provide more error context to SDKs for terminator errors, enabling better retry behavior\n* New `proxy.v1` config type for dynamic service proxies (originally released in 1.7.0)\n* New alert event type for surfacing operational issues to network operators - Beta (originally released in 1.7.0)\n* New Azure Service Bus event sink for streaming controller events, contributed by @ffaraone (originally released in 1.7.0)\n* Bundled ZAC upgraded to 4.0\n* Build updated to Go 1.25\n* CLI cleaned up to remove calls to `os.Exit`, making it more friendly for embedding\n* OIDC is now enabled by default\n* Controller Edge APIs now return `WWW-Authenticate` response headers on `401 Unauthorized` responses, giving clients actionable information about which auth methods are accepted and what went wrong\n* HA Controllers can be marked as 'preferredLeader' via config\n* Dynamic cost range for smart routing expanded beyond the previous 64K limit\n* Dial failures now return the circuit ID and error information for easier debugging\n* Router-to-controller control channels now support multiple underlays with priority-based message routing\n* The dialing identity's ID and name are now forwarded to the hosting SDK\n* Controllers can now dial routers to establish control channels, enabling connectivity when routers are behind firewalls (Beta)\n* Refresh-token revocations are now batched and best-effort, removing the database/raft bottleneck on token refreshes\n* [OIDC discovery endpoint extensions](#oidc-discovery-endpoint-extensions) - OpenZiti-specific endpoint URLs in the OIDC discovery document\n* `ziti edge quickstart` now always runs in HA mode. The `ha` subcommand has been removed. Use\n  `ziti edge quickstart join` to add additional members to the cluster. Note: existing quickstart instances\n  are not compatible with the new HA-only mode and will need to be recreated.\n* The `--clustered` flag on `ziti create config controller` has been removed; the generated config is always\n  cluster-ready. If you have scripts passing `--clustered`, remove it.\n* [Connect events pool](#connect-events-pool) - fixes a goroutine leak when routers reconnect and ensures per-router event ordering\n* [Multiple DNS upstreams](#multiple-dns-upstreams) - the tunneler can now fan out recursive queries to several upstream resolvers in parallel\n\n## OIDC Discovery Endpoint Extensions\n\nThe OIDC discovery document (`/.well-known/openid-configuration`) now includes a vendor-specific\n`openziti_endpoints` field. This lets SDKs discover OpenZiti's custom login and MFA endpoints at\nruntime instead of hardcoding paths.\n\nThe field contains absolute URLs for each endpoint, derived from the issuer the client connected to:\n\n```json\n{\n  \"issuer\": \"https://controller.example.com:1280/oidc\",\n  \"authorization_endpoint\": \"https://controller.example.com:1280/oidc/authorize\",\n  \"token_endpoint\": \"https://controller.example.com:1280/oidc/oauth/token\",\n  \"...other standard OIDC fields...\",\n  \"openziti_endpoints\": {\n    \"password\":           \"https://controller.example.com:1280/oidc/login/password\",\n    \"cert\":               \"https://controller.example.com:1280/oidc/login/cert\",\n    \"ext_jwt\":            \"https://controller.example.com:1280/oidc/login/ext-jwt\",\n    \"totp\":               \"https://controller.example.com:1280/oidc/login/totp\",\n    \"totp_enroll\":        \"https://controller.example.com:1280/oidc/login/totp/enroll\",\n    \"totp_enroll_verify\": \"https://controller.example.com:1280/oidc/login/totp/enroll/verify\",\n    \"auth_queries\":       \"https://controller.example.com:1280/oidc/login/auth-queries\"\n  }\n}\n```\n\n| Key                | Method(s)   | Description                                       |\n|--------------------|-------------|---------------------------------------------------|\n| `password`         | POST        | Username/password authentication                  |\n| `cert`             | POST        | Client certificate authentication                 |\n| `ext_jwt`          | POST        | External JWT authentication                       |\n| `totp`             | POST        | TOTP code verification for MFA                    |\n| `totp_enroll`      | POST/DELETE | Start (POST) or delete (DELETE) TOTP enrollment   |\n| `totp_enroll_verify`| POST       | Verify a TOTP enrollment code                     |\n| `auth_queries`     | GET         | Retrieve pending authentication queries           |\n\nWhen the controller serves edge-oidc on multiple web servers, each discovery response reflects\nthe issuer (and port) the client connected to.\n\n## Connect Events Pool\n\nThe controller now uses per-router, single-worker goroutine pools to process identity\nconnect/disconnect events. Previously each router connection spawned a dedicated\ngoroutine that was never cleaned up on disconnect, leaking a goroutine per reconnect\ncycle. Under churn (e.g., chaos testing with hundreds of routers) this could accumulate\ntens of thousands of leaked goroutines and destabilize the controller.\n\nUsing a single-worker pool per router also ensures that events from the same router are\nalways processed in FIFO order. Previously, a shared multi-worker pool could process a\nfull-state sync after a newer incremental event from the same router, causing identities\nto be incorrectly marked as disconnected.\n\nThe pool is configurable in the controller config file:\n\n```yaml\nconnectEvents:\n  queueSize: 5    # per-router work queue depth (default: 5)\n  idleTime:  30s  # worker idle timeout before exit (default: 30s)\n```\n\nThe defaults are suitable for most deployments. Each router's worker starts on demand\nand exits after the idle timeout, so no goroutines are held when there is no work.\n\nNote: the `minWorkers` and `maxWorkers` settings have been removed. Each router's pool\nis fixed at one worker for correctness.\n\n## Community Contributors\n\nThank you to the following community members for their contributions:\n\n* @ffaraone - Azure Service Bus event sink\n* @dmuensterer - OIDC token refresh fixes\n* @nenkoru - Controller isleader health check endpoint\n* Jan Starkl - UPDB auth attempts fix\n* Mamy Ratsimbazafy - uint16 port range fix\n\n## Basic Permission System (BETA)\n\nAdded a basic permission system that allows more control over identity access to controller management API operations. \nThis replaces the previous binary admin/non-admin model with a more flexible permission system.\n\n**NOTE:** This feature is in BETA, primarily so we can get feedback on which permissions make sense. The implementation is unlikely to change\nbut the set of exposed permissions may grow, shrink or change based on user feedback. \n\n### Permission Model\n\nThe permission system supports three levels of authorization:\n\n  1. **Global Permissions**: System-wide access levels\n     - `admin` - Full access to all operations. This is still controlled by the `isAdmin` flag on identity\n     - `admin_readonly` - Read-only access to all resources except debugging facilities inspect and validate\n\n  2. **Entity-Level Permissions**: Full CRUD access to specific entity types\n     - Granting an entity-level permission (e.g., `service`) provides complete create, read, update, and delete access for that entity type\n\n  3. **Action-Level Permissions**: Specific operation access on entity types\n     - Fine-grained control using the pattern `<entity>.<action>` (e.g., `service.read`, `identity.update`)\n     - Supports `create`, `read`, `update`, and `delete` actions per entity type\n\n### Supported Entity Permissions\n\nThe following entity-level permissions are available:\n\n- `auth-policy` - Authentication policy management\n- `ca` - Certificate Authority management\n- `config` - Configuration management\n- `config-type` - Configuration type management\n- `edge-router-policy` - Edge router policy management\n- `enrollment` - Enrollment management\n- `external-jwt-signer` - External JWT signer management\n- `identity` - Identity management\n- `posture-check` - Posture check management\n- `router` - Edge and transit router management\n- `service` - Service management\n- `service-policy` - Service policy management\n- `service-edge-router-policy` - Service edge router policy management\n- `terminator` - Terminator management\n- `ops` - Operational resources (API sessions, sessions, circuits, links, inspect and validate)\n\n### Permission Assignment\n\nPermissions are assigned to identities via the `permissions` field in the identity resource. Multiple permissions can be granted to a single identity, and permissions are additive.\n\n### Cross-Entity Operations\n\nListing related entities through an entity's endpoints requires appropriate permissions for the related entity type. For example:\n- Listing services for a service-policy requires `service.read` permission\n- Listing identities for an edge-router-policy requires `identity.read` permission\n- Listing configs for a service requires `config.read` permission\n\n**NOTE:** \nMore permissions than expected may be required when performing actions through the CLI or ZAC. Take for example, when an identity \nhas `config.create` and is attempting to create a new config. The CLI may fail if the identity doesn't have `config-type.read`\nas well because it will need to look up the config type id that corresponds to the given config type name.\n\nSimilar cross entity read permissions may be required when creating services.\n\n### Admin Protection\n\nNon-admin identities cannot:\n- Create identities with the `isAdmin` flag\n- Create identities with any permissions granted\n- Modify admin-related fields on existing identities\n- Update or delete admin identities\n- Grant permissions to identities\n\nThese protections ensure that privilege escalation is prevented and admin access remains controlled.\n\n\n## Binding Controller APIs With Identity\n\nController APIs can now be bound to an OpenZiti overlay network identity, allowing secure communication through\nthe Ziti network. This is useful for scenarios where you want to expose controller APIs only through the overlay\nnetwork rather than on a standard network interface.\n\n### Configuration Structure\n\nA standard `bindPoint` configuration looks like this:\n```text\n    bindPoints:\n      - interface: 127.0.0.1:18441\n        address: 127.0.0.1:18441\n```\n\nTo bind controller APIs to an OpenZiti identity, add an additional `identity` block to your `bindPoints`. The\nidentity configuration specifies where to load the Ziti identity file and which service to bind it to:\n\n```text\n    bindPoints:\n      - interface: 127.0.0.1:18441\n        address: 127.0.0.1:18441\n      - identity:\n          file: \"c:/temp/ctrl.testing/ctrl.identity.json\"\n          service: \"mgmt\"\n```\n\n### Supported Configuration Options\n\n- `file`: Path to a Ziti identity JSON file containing the controller's identity and enrollment certificate\n- `env`: Name of an environment variable containing a base64-encoded Ziti identity (alternative to `file`)\n- `service`: The name of the Ziti service to bind the controller API to\n\n### Using Environment Variables\n\nFor deployments where storing identity files on disk is not preferred, you can reference a base64-encoded\nidentity file from an environment variable. The environment variable should contain the base64-encoded contents\nof the identity JSON file.\n\nFor example, if an environment variable named `ZITI_CTRL_IDENTITY` contains a base64-encoded identity file:\n\n```text\n    bindPoints:\n      - interface: 127.0.0.1:18441\n        address: 127.0.0.1:18441\n      - identity:\n          env: ZITI_CTRL_IDENTITY\n          service: \"mgmt\"\n```\n\n### IPv6 Support\n\nBoth IPv4 and IPv6 addresses are supported for standard bind points. IPv6 addresses should be specified in bracket\nnotation with a port number:\n\n```text\n    bindPoints:\n      - interface: \"[::1]:18441\"\n        address: \"[::1]:18441\"\n      - identity:\n          file: \"/path/to/identity.json\"\n          service: \"mgmt\"\n```\n\n## CLI Enhancements for Identity-Based Connections\n\nThe `ziti edge login` command and REST client utilities have been enhanced to support identity-based connections\nthrough the Ziti overlay network.\n\n### New `--network-identity` Flag for `ziti edge login`\n\nThe `ziti edge login` command now includes a `--network-identity` flag that allows you to authenticate to a Ziti\ncontroller through the overlay network using a Ziti identity:\n\n```bash\nziti edge login https://ziti.mgmt.apis.local:1280 \\\n  --username myuser \\\n  --password mypass \\\n  --network-identity /path/to/identity.json\n```\n\nThis is useful when the controller is only accessible through the Ziti overlay network or when you want to ensure\nall communication to the controller flows through the overlay for security purposes.\n\n### Identity Resolution Order\n\nWhen establishing connections, identities are resolved in the following order:\n\n1. **Command-line flag**: The `--network-identity` flag takes precedence\n2. **Environment variable**: If `ZITI_CLI_NETWORK_ID` is set and contains a base64-encoded identity, it is used\n3. **Cached identity file**: If a network identity was saved from a previous login in the Ziti config directory, it may be used\n\nThis layered approach allows for flexibility in deployment scenarios:\n- Development: Use command-line flags for quick testing\n- Automation: Use environment variables in CI/CD pipelines\n- Production: Cache identities securely for repeated access\n\n#### Dialing Modes When Authenticating\n\nThe CLI supports two dialing modes:\n\n**Intercept-based Dialing (Default)**\nBy default, URLs are expected to leverage intercepts. Create a service with an appropriate intercept config and use\nthe intercept address when dialing. This is the standard mode for most use cases. For example, given a service with\nthe intercept `ziti.mgmt.apis.local`\n```bash\nziti edge login https://ziti.mgmt.apis.local:1280 \\\n  --username myuser \\\n  --password mypass \\\n  --network-identity /path/to/identity.json\n```\n\n**Identity-aware Dialing (Addressable Terminators)**\nTo support addressable terminators-based dialing, specify a user in the URL. This activates dial-by-identity\nfunctionality. The URL format should be `identity-to-dial@service-name-to-dial`. For example:\n```bash\nziti edge login https://my-identity@my-service:1280 \\\n  --username myuser \\\n  --password mypass \\\n  --network-identity /path/to/identity.json\n```\n\nIn this mode, the transport extracts the identity from the URL and uses it to establish a direct connection to\nthe specified service via the addressable terminator.\n\n\n## OIDC/JWT Token-based Enrollment\n\nOpenZiti now supports provisioning identities just-in-time through OIDC/JWT token enrollment. External identity \nproviders can be configured to allow identities to enroll using JWT tokens, with support for the resulting \nidentities to use certificate or token authentication.\n\n### External JWT Signer Configuration\n\nExternal JWT signers are configured via the Edge Management API to define enrollment behavior with the following new \nenrollment-specific properties:\n\n- **enrollToCertEnabled** - When enabled, identities can exchange a JWT token and a certificate signing request (CSR) \n        for a client certificate during enrollment. The certificate can then be used for standard certificate-based\n        authentication.\n\n- **enrollToTokenEnabled** - When enabled, identities can use a JWT token to enroll. The current token or future tokens\n        may be used for authentication.\n\n- **enrollNameClaimsSelector** - Specifies which JWT claim contains the identity name. Accepts a JSON pointer \n        (e.g., `/preferred_username`) or a simple property name (e.g., `preferred_username`, automatically converted to\n        `/preferred_username`). Defaults to `/sub` if not specified. The extracted value becomes the identity name in Ziti.\n\n- **enrollAttributeClaimsSelector** - Specifies which JWT claims to extract as identity attributes during enrollment.\n        Accepts a JSON pointer (e.g., `/roles`) or a simple property name (e.g., `roles`). Extracted attributes are \n        applied to the newly enrolled identity for use in authorization policies.\n\n- **enrollAuthPolicyId** - Specifies the authentication policy to apply to newly enrolled identities. This determines\n        what authentication methods are available for the identity post-enrollment.\n\nAdditionally the existing property named **claimsProperty** that specifies external id to match identities to:\n\n- now supports a JSON pointer (e.g., `/id`) or a simple property name (e.g., `id`)\n- is used to populate the `externalId` field of the identity\n\n### Enrollment Paths\n\n#### Certificate Enrollment (enrollToCertEnabled)\n\nWhen certificate enrollment is enabled, unauthenticated users can:\n\n1. Obtain a list of available IdPs from the public Edge Client API `GET /external-jwt-signers` endpoint, where \n   `enrollToCertEnabled` is set to `true`\n2. Obtain a JWT from the configured OIDC provider\n3. Generate a certificate signing request (CSR)\n4. Submit an enrollment request with the JWT and CSR\n5. Have their identity created in Ziti with attributes extracted from JWT claims\n6. Receive a signed client certificate for certificate-based authentication\n\n#### Token Enrollment (enrollToTokenEnabled)\n\nWhen token enrollment is enabled, unauthenticated users can:\n\n1. Obtain a list of available IdPs from the public Edge Client API `GET /external-jwt-signers` endpoint, where \n   `enrollToTokenEnabled` is set to `true`\n1. Obtain a JWT from the configured OIDC provider\n2. Submit an enrollment request with the JWT\n3. Have their identity created in Ziti with attributes extracted from JWT claims\n4. Receive a Ziti API token for token-based authentication\n\n### Edge Management API\n\nThe Edge Management API provides full CRUD operations for configuring external JWT signers:\n\n- `POST /external-jwt-signers` - Create a new external JWT signer with all configuration options\n- `GET /external-jwt-signers` - List all configured external JWT signers\n- `GET /external-jwt-signers/{id}` - Retrieve a specific signer configuration\n- `PUT /external-jwt-signers/{id}` - Update all fields of a signer\n- `PATCH /external-jwt-signers/{id}` - Partially update a signer\n- `DELETE /external-jwt-signers/{id}` - Delete a signer\n\n### Edge Client API\n\nThe Edge Client API exposes a reduced set of external JWT signer information for unauthenticated enrollment requests:\n\n- `GET /external-jwt-signers` - List available JWT signers with enrollment capabilities\n\nThe client API response includes the following fields for each signer:\n\n- `name` - Signer name\n- `externalAuthUrl` - URL where users obtain JWT tokens\n- `clientId` - OIDC client ID\n- `scopes` - Requested OIDC scopes\n- `openIdConfigurationUrl` - OIDC discovery endpoint\n- `audience` - Expected token audience\n- `targetToken` - Token type to use (ACCESS or ID)\n- **`enrollToCertEnabled`** - Flag indicating certificate enrollment is available\n- **`enrollToTokenEnabled`** - Flag indicating token enrollment is available\n\n### CLI Commands\n\n**Create an external JWT signer with enrollment options:**\n```\nziti edge controller create ext-jwt-signer <name> <issuer> \\\n  --jwks-endpoint <url> \\\n  --audience <audience> \\\n  --enroll-to-cert \\\n  --enroll-to-token=false \\\n  --enroll-name-claims-selector preferred_username \\\n  --enroll-attr-claims-selector roles \\\n  --enroll-auth-policy <policy-id-or-name>\n```\n\n**Update enrollment options on an existing signer:**\n```\nziti edge controller update ext-jwt-signer <name|id> \\\n  --enroll-to-cert \\\n  --enroll-auth-policy <policy-id-or-name>\n```\n\n**List external JWT signers:**\n```\nziti edge controller list ext-jwt-signers\n```\n\n## Clustering Performance Improvements\n\nIn previous releases, model updates were submitted to raft one at at time. This prevented \nraft from being efficient by allowing command batching. This release allows multiple \nmodel updates to be in-flight at the same time. \n\nNew Configuration Options\n\n1. Raft Apply Timeout (cluster.applyTimeout)\n\nLocation: Controller configuration file, under the cluster section\nType: Duration\nDefault: 5s\nDescription: Timeout for applying commands to the Raft distributed log. Commands that exceed this timeout will trigger adaptive rate limiter backoff.\n\nExample:\n```\ncluster:\n  applyTimeout: 10s\n```\n\n2. Cluster Rate Limiter Configuration (cluster.rateLimiter)\n\nA new adaptive rate limiter that controls the submission of commands to the Raft cluster. Unlike the existing command rate limiter, this specifically manages in-flight Raft operations with adaptive window sizing.\n\nConfiguration Structure:\n```\ncluster:\n  rateLimiter:\n    enabled: true\n    minSize: 5\n    maxSize: 250\n    timeout: 30s\n```\n\nSub-options:\n\n  - enabled (boolean)\n    - Default: true\n    - Description: Enable/disable adaptive rate limiting for Raft command submission\n  - minSize (integer)\n    - Default: 5\n    - Minimum: 1\n    - Description: Minimum window size for concurrent in-flight Raft operations\n  - maxSize (integer)\n    - Default: 250\n    - Description: Maximum window size for concurrent in-flight Raft operations. Must be >= minSize\n  - timeout (duration)\n    - Default: 30s\n    - Description: Time after which outstanding work is assumed to have failed if not marked completed\n\n3. Restart Self on Snapshot (cluster.restartSelfOnSnapshot)\n\nLocation: Controller configuration file, under cluster section\nType: Boolean\nDefault: false\nDescription: When true, the controller will automatically restart itself when restoring a snapshot to an initialized system. When false, the controller will exit with code 0, requiring external process management to restart it.\n\nExample:\n```\ncluster:\n    restartSelfOnSnapshot: true\n```\n\n### New Metrics\n\nThe adaptive rate limiter exposes three new metrics:\n\n  1. raft.rate_limiter.queue_size (gauge)\n    - Current number of operations queued/in-flight\n  2. raft.rate_limiter.work_timer (timer)\n    - Duration of rate-limited operations\n  3. raft.rate_limiter.window_size (gauge)\n    - Current adaptive window size\n\n## Rate Limiter Algorithm Improvements\n\nThe adaptive rate limiter tracker now uses a success rate metric to decide when to grow or shrink its\nconcurrency window, instead of using raw queue position. An exponentially decaying histogram tracks the\nratio of successes to backoffs. When the success rate exceeds a configurable threshold, the window is\ngrown by a multiplicative increase factor. When it falls below the threshold, the window is shrunk by a\nmultiplicative decrease factor. The check intervals for increase and decrease are independently configurable.\n\nThis approach produces smoother, more stable window adjustments under varying load, avoiding the\nover-aggressive shrinking that could occur when queue position alone was used as the signal.\n\nThis specific rate limiter implementation is used in three places:\n* **Controller TLS handshake rate limiting** - controls the rate of incoming TLS handshakes on the controller\n* **Raft command submission** - controls the rate of commands submitted to the Raft distributed log\n* **Router control channel rate limiting** - controls the rate of requests from the router to the controller\n\nNote: this work was done in advance of enabling the TLS handshake rate limiter by default. The TLS\nhandshake rate limiter is not yet enabled by default, but can be enabled via the `tls.rateLimiter`\nconfiguration section.\n\nNew configuration options (available under each `rateLimiter` section):\n\n  - successThreshold (float)\n    - Default: 0.9\n    - Description: Success rate threshold above which the window size will be increased and below which it will be decreased\n  - increaseFactor (float)\n    - Default: 1.02\n    - Description: Multiplier applied to the current window size when growing. Must be greater than 1\n  - decreaseFactor (float)\n    - Default: 0.9\n    - Description: Multiplier applied to the current window size when shrinking. Must be between 0 and 1\n  - increaseCheckInterval (integer)\n    - Default: 10\n    - Description: Number of successes between window size increase checks\n  - decreaseCheckInterval (integer)\n    - Default: 10\n    - Description: Number of backoffs between window size decrease checks\n\n## Background Processing for Identity Updates\n\nIdentity environment and authenticator updates that occur during authentication are now processed asynchronously in the background. \nThis prevents authentication requests from blocking when the system is under load, significantly improving resilience during thundering herd scenarios.\n\nWhen the background queue fills up, updates can be dropped as they will be refreshed on the next authentication attempt. \nThis allows the system to gracefully handle load spikes without impacting authentication performance.\n\nFor now, dropping entries when the queue fills will be disabled by default, but can be enabled, see below.\n\n### Configuration\n\nA new `command.background` configuration section controls the background processing behavior:\n\n```yaml\n  command:\n    background:\n      enabled: true           # Enable background processing (default: true)\n      queueSize: 1000        # Maximum queue size (default: 1000)\n      dropWhenFull: true     # Drop updates when queue is full (default: false)\n      delayThreshold: 50ms   # The threshold for how long updates are taking before starting to background updates\n```\n\nNote that the `commandRateLimiter` configuration section may instead be specified under `command` as `rateLimiter`.\n\nExample:\n\n```yaml\n  command:\n    background:\n      enabled: true\n      queueSize: 250\n      dropWhenFull: false\n      delayThreshold: 50ms\n    rateLimiter:\n      enabled:   true\n      maxQueued: 25\n```\n\nNote that if command rate limiter configuration is specified in both locations, the settings under `command` will take \nprecedence. The standalone `commandRateLimiter` section may be deprecated in the future.\n\n### Metrics\n\nWhen background processing is enabled, the following metrics are exposed:\n\n- command.background.queue_size - Current number of queued background tasks\n- command.background.worker_count - Current number of worker goroutines\n- command.background.busy_workers - Number of workers currently processing tasks\n- command.background.work_timer - Timer tracking background task execution (includes histogram, meter, and count)\n- command.background.dropped_entries - Count of dropped updates when queue is full (only when dropWhenFull is enabled)\n\n## New proxy.v1 Config Type\n\n*Originally released in 1.7.0*\n\nAdded support for dynamic service proxies with configurable binding and protocol options.\nThis allows Edge Routers and Tunnelers to create proxy endpoints that can forward traffic for Ziti services.\n\nThis differs from intercept.v1 in that intercept.v1 will intercept traffic on specified\nIP addresses or DNS entries to forward to a service using tproxy or tun interface,\ndepending on implementation.\n\nA proxy on the other hand will just start a regular TCP/UDP listener on the configured port,\nso traffic will have to be configured for that destination.\n\nExample proxy.v1 Configuration:\n\n```\n  {\n    \"port\": 8080,\n    \"protocols\": [\"tcp\"],\n    \"binding\": \"0.0.0.0\"\n  }\n```\n\nConfiguration Properties:\n  - port (required): Port number to listen on (1-65535)\n  - protocols (required): Array of supported protocols (tcp, udp)\n  - binding (optional): Interface to bind to. For the ER/T defaults to the configured lanIF config property.\n\nThis config type is currently supported by the ER/T when running in either proxy or tproxy mode.\n\n## Alert Events (BETA)\n\n*Originally released in 1.7.0*\n\nA new alert event type has been added to allow Ziti components to emit alerts for issues that network operators can address.\nAlert events are generated when components encounter problems such as service configuration errors or resource\navailability issues.\n\nAlert events include:\n  - Alert source type and ID (currently supports routers, with controller and SDK support planned for future releases)\n  - Severity level (currently supports error, with info and warning planned for future releases)\n  - Alert message and supporting details\n  - Related entities (router, identity, service, etc.) associated with the alert\n\nExample alert event when a router cannot bind a configured network interface:\n\n```\n  {\n    \"namespace\": \"alert\",\n    \"event_src_id\": \"ctrl1\",\n    \"timestamp\": \"2021-11-08T14:45:45.785561479-05:00\",\n    \"alert_source_type\": \"router\",\n    \"alert_source_id\": \"DJFljCCoLs\",\n    \"severity\": \"error\",\n    \"message\": \"error starting proxy listener for service 'test'\",\n    \"details\": [\n      \"unable to bind eth0, no address\"\n    ],\n    \"related_entities\": {\n      \"router\": \"DJFljCCoLs\",\n      \"identity\": \"DJFljCCoLs\",\n      \"service\": \"3DPjxybDvXlo878CB0X2Zs\"\n    }\n  }\n```\n\nAlert events can be consumed through the standard event system and logged to configured event handlers for monitoring and alerting purposes.\n\nThese events are currently in Beta, as the format is still subject to change. Once they've been in use in production for a while\nand proven useful, they will be marked as stable.\n\n## Azure Service Bus Event Sink\n\n*Originally released in 1.7.0. Contributed by @ffaraone.*\n\nAdds support for streaming controller events to Azure Service Bus.\nThe new logger enables real-time event streaming from the OpenZiti controller to Azure Service Bus\nqueues or topics, providing integration with Azure-based monitoring and analytics systems.\n\nTo enable the Azure Service Bus event logger, add configuration to the controller config file under the events section:\n\n```\n  events:\n    serviceBusLogger:\n      subscriptions:\n        - type: circuit\n        - type: session\n        - type: metrics\n          sourceFilter: .*\n          metricFilter: .*\n        # Add other event types as needed\n      handler:\n        type: servicebus\n        format: json\n        connectionString: \"Endpoint=sb://your-namespace.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=your-key\"\n        topic: \"ziti-events\"          # Use 'topic' for Service Bus topic\n        # queue: \"ziti-events-queue\"  # Or use 'queue' for Service Bus queue\n        bufferSize: 100                # Optional, defaults to 50\n```\n\n- Required configuration:\n    - format: Event format, currently supports only json\n    - connectionString: Azure Service Bus connection string\n    - Either topic or queue: Destination name (mutually exclusive)\n\n- Optional configuration:\n    - bufferSize: Internal message buffer size (default: 50)\n\n## OIDC is now enabled by default\n\nThe controller now automatically adds the `edge-oidc` API binding to any web listener that hosts\nthe `edge-client` API, even when `edge-oidc` is not explicitly listed in the `web` section of the\nconfiguration. This means OIDC-based authentication is available out of the box without requiring\nchanges to existing controller configurations.\n\n### Where OIDC binds\n\nThe `edge-oidc` binding is added to the same web listener(s) as `edge-client`. If `edge-client` is\nhosted on `127.0.0.1:1280`, the OIDC endpoints will be available on that same address under the\n`/oidc` path (e.g. `https://127.0.0.1:1280/oidc/.well-known/openid-configuration`).\n\nThe controller capabilities returned by `GET /version` will include `OIDC_AUTH` and the\n`edge-oidc` entry will be present in `apiVersions` when OIDC is active.\n\n### Opting out\n\nIf OIDC should not be enabled automatically, set `disableOidcAutoBinding: true` under `edge.api`:\n\n```yaml\nedge:\n  api:\n    address: 127.0.0.1:1280\n    sessionTimeout: 30m\n    disableOidcAutoBinding: true\n```\n\nWhen `disableOidcAutoBinding` is `true`, OIDC will only be active if `edge-oidc` is explicitly\nlisted as a binding in the `web` section. \n\nWhen OIDC is not enabled, all (SDK) clients will revert to using legacy authentication.\nLegacy authentication does not support multiple controllers or HA in general. Clients will treat\ntheir controller as if it is a single controller instance and will function as if no other controllers\nexist.\n\n\n## WWW-Authenticate Headers\n\nThe controller's Edge APIs now include `WWW-Authenticate` headers on `401 Unauthorized` responses,\nper issuer: https://github.com/openziti/ziti/issues/3356. These headers provide insight into why\na token was rejected. The main benefit of these headers is to convey information for JWT backed\nAPI Sessions and API Session authentication where a token may not have been provided (missing),\nis used beyond its expiration date (expired), or the token has become invalid for any other\nreason (invalid).\n\n`www-authenticate` headers are provided as a single header instance with multiple challenge values\nseparate by commas.\n\n### No Credentials Provided\n\nWhen a request hits a protected endpoint without any credentials, a single `WWW-Authenticate` header\nis returned listing both accepted auth schemes as comma-separated challenges:\n\n```\nWWW-Authenticate: zt-session realm=\"zt-session\" error=\"missing\" error_description=\"no matching token was provided\",Bearer realm=\"openziti-oidc\" error=\"missing\" error_description=\"no matching token was provided\"\n```\n\n### Token Errors\n\nWhen a token is present but cannot be accepted, the header identifies the scheme and what went wrong:\n\n```\nWWW-Authenticate: Bearer realm=\"openziti-oidc\" error=\"expired\" error_description=\"token expired\"\nWWW-Authenticate: Bearer realm=\"openziti-oidc\" error=\"invalid\" error_description=\"token is invalid\"\nWWW-Authenticate: zt-session realm=\"zt-session\" error=\"invalid\" error_description=\"token is invalid\"\n```\n\n### OIDC External JWT — Primary Authentication\n\nWhen an auth policy requires an external JWT signer for primary authentication (e.g., a PKCE flow\nbacked by an ext-jwt signer), the header identifies which signers are accepted and what went wrong.\nMultiple accepted signers are pipe-delimited in the `id` and `issuer` parameters:\n\n```\nWWW-Authenticate: Bearer realm=\"openziti-primary-ext-jwt\" error=\"missing\" error_description=\"no matching token was provided\" id=\"signer-id-1|signer-id-2\" issuer=\"https://issuer1.example.com|https://issuer2.example.com\"\n```\n\nThe `error` value follows the same `missing`/`expired`/`invalid` pattern as standard bearer token errors.\n\n### OIDC External JWT — Secondary / MFA Authentication\n\nWhen an auth policy requires an external JWT signer as a secondary factor (step-up after primary\nauth succeeds), the header identifies the single required signer. The `error` value follows the\nsame `missing`/`expired`/`invalid` pattern:\n\n```\nWWW-Authenticate: Bearer realm=\"openziti-secondary-ext-jwt\" error=\"missing\" error_description=\"no matching token was provided\" id=\"<signer-id>\" issuer=\"<issuer>\"\n```\n\n### Anonymous Endpoints\n\nUnauthenticated endpoints such as version information do not return `WWW-Authenticate` headers.\n\n## HA Preferred Leaders\n\nControllers can be marked as a preferred leader. \n\n**Example Config**\n```yaml\ncluster:\n  dataDir: /home/{{ .Model.MustVariable \"credentials.ssh.username\" }}/fablab/ctrldata\n  preferredLeader: true\n```\n\nIf a controller that is not marked preferredLeader becomes a preferredLeader, it will check \nif there's a node available that is marked as preferred. If there is one, or one later\njoins the cluster, the non-preferred node will attempt to transfer leadership to the \nnode that is marked as preferred.\n\n## Expanded Dynamic Cost Range\n\nThe dynamic cost range for smart routing has been expanded beyond the previous 64K limit. Under high\nload, terminators could saturate the cost space, making dynamic cost values meaningless for routing\ndecisions and leading to uneven load distribution. The expanded range allows for more granular cost\ndifferentiation even under heavy load.\n\n## Circuit ID and Error in Dial Failures\n\nDial failures now return the circuit ID and, when available, the error that caused the circuit to fail.\nPreviously, the circuit ID was only returned on successful dials. Note that SDKs will need to be\nupdated to surface the circuit id when a dial failure happens.\n\n## Multi-Underlay Control Channels\n\nRouter-to-controller control channels now support multiple underlays with priority-based message routing.\nThis allows time-sensitive control messages (heartbeats, routing, circuit requests) to be separated from\noperational data (metrics, inspections) across dedicated TCP connections, preventing bulk operations from\ndelaying user-affecting control plane traffic. This feature does not yet allow specifying multiple \nnetwork interfaces to use, to load balance data across.\n\n## Dialing Identity Forwarded to Hosting SDK\n\nThe identity ID and name of the dialing client are now forwarded to the hosting SDK when a circuit is\nestablished. This allows hosting applications to identify which identity initiated the connection,\nenabling identity-aware request handling on the server side. This will require SDK updates to add this\nto the API for hosting applications.\n\n## Controller-Initiated Control Channel Dials (BETA)\n\nControllers can now dial routers to establish control channels. Previously, routers were solely\nresponsible for dialing controllers. This feature is designed for deployments where one or more\ncontrollers are in a private network that routers cannot reach, but the controllers can dial out.\nA common scenario is an HA cluster where some controllers are publicly reachable and some are in\na private network. External routers connect to the public controllers normally, and the private\ncontrollers dial out to the routers.\n\n### Router Configuration\n\nRouters can configure one or more control channel listeners. Each listener specifies a bind address,\nan advertise address (reported to the controller), and optional groups for matching.\n\n```yaml\nctrl:\n  listeners:\n    - bind: tls://0.0.0.0:6262\n      advertise: tls://router.example.com:6262\n      groups:\n        - default\n```\n\nThe router is the authoritative source of ctrl channel listener information, similar to link\nlisteners. When a router connects to any controller, it reports its configured `ctrlChanListeners`\nand the controller data model is updated automatically. This means that in most deployments —\nwhere the router can reach at least one controller — no manual configuration of listener addresses\nis needed on the controller side.\n\nThe `ctrlChanListeners` field can also be set via the CLI for cases where a router cannot reach\nany controller and thus cannot initialize the data model itself:\n\n```bash\nziti edge update edge-router myRouter --bootstrap-ctrl-chan-listener 'tls://router.example.com:6262=group1,group2'\n```\n\nGroups default to `[\"default\"]` if not specified.\n\n### Controller Configuration\n\nThe controller dialer is disabled by default and must be explicitly enabled. When enabled, the\ncontroller will dial routers that have control channel listeners configured and are not already\nconnected.\n\n```yaml\nctrl:\n  dialer:\n    enabled: true\n    groups:\n      - default\n    dialDelay: 30s\n    minRetryInterval: 1s\n    maxRetryInterval: 5m\n    retryBackoffFactor: 1.5\n    fastFailureWindow: 5s\n    queueSize: 32\n    maxWorkers: 10\n```\n\n- `enabled` - Enables the controller dialer (default: `false`)\n- `groups` - List of groups to match against router listener groups (default: `[\"default\"]`)\n- `dialDelay` - Delay before the controller starts dialing after boot (default: `30s`)\n- `minRetryInterval` - Minimum backoff delay between dial retries (default: `1s`)\n- `maxRetryInterval` - Maximum backoff delay between dial retries (default: `5m`)\n- `retryBackoffFactor` - Multiplier applied to the retry delay on each failure, jittered +/- 0.5 (default: `1.5`)\n- `fastFailureWindow` - If a connection drops within this window after connecting, backoff continues rather than resetting (default: `5s`)\n- `queueSize` - Maximum number of pending dial jobs in the worker pool queue (default: `32`)\n- `maxWorkers` - Maximum number of concurrent dial workers (default: `10`)\n\nThe controller will only dial routers whose listener groups overlap with the controller's configured\ngroups. When a router advertises multiple ctrl channel listener addresses, the dialer rotates through\nthem on each failure so that an unreachable address does not block attempts to the others.\n\n### Metrics\n\nThe controller dialer worker pool exposes the following metrics under the `ctrl_channel.dialer` prefix:\n\n- `ctrl_channel.dialer.queue_size` - Current number of pending dial jobs in the queue\n- `ctrl_channel.dialer.worker_count` - Current number of dial worker goroutines\n- `ctrl_channel.dialer.busy_workers` - Number of workers currently executing a dial\n- `ctrl_channel.dialer.work_timer` - Timer tracking the duration of each dial attempt\n\n## SDK Inspection Support\n\nNew CLI commands have been added for inspecting SDK state, useful for diagnosing terminator and\nconnectivity issues.\n\n**Note:** SDK inspection requires SDK support. Currently only the Go SDK supports inspection,\nas of version 1.5.0. Other SDKs will need to add support before these commands can be used\nwith them.\n\n### `ziti fabric inspect sdk`\n\nRetrieves SDK context inspection data from identities connected to routers.\n\n```\nziti fabric inspect sdk <target-selector> <identity-id>\n```\n\nThe `<target-selector>` is a regex matching router IDs (use `.*` for all routers). The\n`<identity-id>` is the identity whose SDK context you want to inspect. The command returns\ndetailed state from the connected SDK instance, including active services, terminators, and\nconnection status.\n\n### `ziti agent tunnel dump-sdk`\n\nDumps SDK context information from a running `ziti tunnel` process via the IPC agent.\n\n```\nziti agent tunnel dump-sdk\n```\n\nReturns JSON-formatted inspection data for all SDK contexts registered in the tunnel process,\nincluding service listeners, connections, and terminator state.\n\n## Revocation System Improvements\n\nWhen a session is refreshed, the old refresh token's revocation is no longer created\nsynchronously through raft. Instead, revocations are queued in memory and flushed in\nbatches on a configurable interval. This removes the database and raft as a bottleneck\non token refreshes. If the old token is close to expiring, the revocation is skipped\nentirely.\n\nNew configuration tunables under `edge.oidc`:\n\n| Key | Default | Description |\n|-----|---------|-------------|\n| `revocationMinTokenLifetime` | unset | Skip revocation if the old token expires within this duration (must be < 50% of `refreshTokenDuration`) |\n| `revocationBucketInterval` | `1m` | Bucket window for batching revocations before flushing through raft |\n| `revocationBucketMaxSize` | `200` | Max revocations per raft entry |\n| `revocationMaxQueued` | `25000` | Max revocations queued in memory before dropping |\n| `revocationEnforcerFrequency` | `1m` | How often expired revocations are purged (leader only) |\n\n## CLI Reorganization Details\n\nThe CLI has been reorganized so that edge and fabric entity management commands are \navailable directly at the top level. The `ziti edge` and `ziti fabric` command trees \nremain fully functional — the new top-level commands are additional entry points, not \nreplacements.\n\n### Top-Level CRUD Commands\n\nEntity create, delete, list, and update operations that previously required the \n`ziti edge` or `ziti fabric` prefix are now available directly:\n\n| New command | Previous command |\n|---|---|\n| `ziti create identity ...` | `ziti edge create identity ...` |\n| `ziti delete service ...` | `ziti edge delete service ...` |\n| `ziti list edge-routers` | `ziti edge list edge-routers` |\n| `ziti update identity ...` | `ziti edge update identity ...` |\n| `ziti list circuits` | `ziti fabric list circuits` |\n| `ziti delete terminator ...` | `ziti fabric delete terminator ...` |\n\nAll edge and fabric entities are available under the consolidated commands. When an \nentity name exists in both edge and fabric (e.g., `service`, `router`), the edge \nversion is the default. Fabric equivalents are accessible with a `fabric-` prefix \n(e.g., `ziti list fabric-services`).\n\n### Top-Level Login\n\nSession management is now available at the top level:\n\n| New command | Previous command |\n|---|---|\n| `ziti login` | `ziti edge login` |\n| `ziti login forget` | `ziti edge login forget` |\n| `ziti login use` | `ziti edge use` |\n\n### Breaking Change: `ziti create ca`\n\n`ziti create ca` now creates a Ziti edge Certificate Authority, matching the \nbehavior of `ziti edge create ca`. Previously, `ziti create ca` was a PKI command \nfor generating CA certificates on the local filesystem.\n\nThe PKI command is still available at its original location:\n\n```\nziti pki create ca ...\n```\n\nScripts that use `ziti create ca` for PKI operations should be updated to use \n`ziti pki create ca` instead.\n\n## Multiple DNS Upstreams\n\nThe tunneler's DNS resolver now accepts multiple upstream DNS servers and fans out recursive queries\nto all of them in parallel, rather than being limited to a single upstream. This is useful for split-horizon\nsetups where different resolvers are authoritative for different zones, and for environments where a primary\nresolver may be slow or unreachable.\n\n### CLI\n\nThe `ziti tunnel --dnsUpstream` flag is now a repeatable string slice. Upstreams can be listed by repeating\nthe flag or by passing a comma-separated value:\n\n```bash\nziti tunnel run \\\n  --dnsUpstream udp://10.96.0.10:53 \\\n  --dnsUpstream tcp://8.8.8.8:53\n```\n\n### Router Config\n\nIn `xgress_edge_tunnel` router configs, `options.dnsUpstream` now accepts either a single string (as before)\nor a list of strings. Existing configs continue to work unchanged:\n\n```yaml\n# single upstream (unchanged)\noptions:\n  dnsUpstream: udp://10.96.0.10:53\n\n# multiple upstreams\noptions:\n  dnsUpstream:\n    - udp://10.96.0.10:53\n    - tcp://8.8.8.8:53\n```\n\n### How Resolution Works\n\nWhen a query comes in, the resolver dispatches it to every configured upstream concurrently. The first\nresponse with `RCODE=NOERROR` wins and is returned to the client immediately, so split-horizon lookups\nwork even if one upstream is slow. If no upstream returns NOERROR, the resolver picks the best-ranked\nnon-NOERROR reply (NXDOMAIN > SERVFAIL > REFUSED) so authoritative negative answers aren't masked by\ntransport failures. If every upstream fails to respond, the query is treated as unanswerable and handled\nper the configured `dnsUnanswerable` disposition.\n\n## Agent Inspect\n\nThe `ziti agent` CLI now has a generic `inspect` subcommand that works against any ziti process\n(controller, router, or tunneler) over the local IPC agent channel. It sends the inspect request\ndirectly to the target process, so unlike `ziti fabric inspect` it doesn't fan out through the\ncontroller and doesn't require network connectivity to the target.\n\n```\nziti agent inspect <value> [values...]\n```\n\nValues are matched against whatever the target process exposes. Common inspect keys:\n\n* Routers: `stackdump`, `links`, `config`, `metrics`, `sdk-terminators`, `ert-terminators`,\n  `router-circuits`, `router-data-model`, `router-controllers`\n* Controllers: `stackdump`, `config`, `metrics`, `connected-routers`, `connected-peers`,\n  `cluster-config`, `router-messaging`, `terminator-costs`, `data-model-index`\n* Tunnelers: `stackdump`, `sdk`\n\n## Current Beta Features\n\nBeta features are still under development and are subject to change. They should\nbe usable in their released form. Though unlikely, there is a small chance they will \nbe removed. \n\n* Basic Permission System\n* Alert Events\n* Controller-Initiated Control Channel Dials\n\n## Component Updates and Bug Fixes\n\n* github.com/openziti/agent: [v1.0.31 -> v1.0.33](https://github.com/openziti/agent/compare/v1.0.31...v1.0.33)\n* github.com/openziti/channel/v4: [v4.2.28 -> v4.3.11](https://github.com/openziti/channel/compare/v4.2.28...v4.3.11)\n    * [Issue #242](https://github.com/openziti/channel/issues/242) - Reconnecting channel shouldn't allow changing ids\n    * [Issue #235](https://github.com/openziti/channel/issues/235) - Bump allowed hello message headers size to 16k from 4k\n    * [Issue #228](https://github.com/openziti/channel/issues/228) - Ensure that Underlay never return nil on MultiChannel\n    * [Issue #226](https://github.com/openziti/channel/issues/226) - Allow specifying a minimum number of underlays for a channel, regardless of underlay type\n    * [Issue #225](https://github.com/openziti/channel/issues/225) - Add ChannelCreated to the UnderlayHandler API to allow handlers to be initialized with the channel before binding\n    * [Issue #224](https://github.com/openziti/channel/issues/224) - Update the underlay dispatcher to allow unknown underlay types to fall through to the default\n    * [Issue #222](https://github.com/openziti/channel/issues/222) - Allow injecting the underlay type into messages\n\n* github.com/openziti/edge-api: [v0.26.47 -> v0.28.1](https://github.com/openziti/edge-api/compare/v0.26.47...v0.28.1)\n    * [Issue #175](https://github.com/openziti/edge-api/issues/175) - ctrlChanListeners should have x-omit-empty: false attribute\n    * [Issue #170](https://github.com/openziti/edge-api/issues/170) - Add preferredLeader flag to controllers\n    * [Issue #167](https://github.com/openziti/edge-api/issues/167) - Add ctrlChanListeners to router types\n    * [Issue #164](https://github.com/openziti/edge-api/issues/164) - Add permissions list to identity\n\n* github.com/openziti/foundation/v2: [v2.0.72 -> v2.0.90](https://github.com/openziti/foundation/compare/v2.0.72...v2.0.90)\n    * [Issue #472](https://github.com/openziti/foundation/issues/472) - Add support for multi-bit set/get to AtomicBitSet\n    * [Issue #464](https://github.com/openziti/foundation/issues/464) - Add support for -pre in versions\n    * [Issue #455](https://github.com/openziti/foundation/issues/455) - Correctly close goroutine pool when external close is signaled\n    * [Issue #452](https://github.com/openziti/foundation/issues/452) - Goroutine pool with a min worker count of 1 can drop to 0 workers due to race condition\n\n* github.com/openziti/identity: [v1.0.111 -> v1.0.128](https://github.com/openziti/identity/compare/v1.0.111...v1.0.128)\n    * [Issue #68](https://github.com/openziti/identity/issues/68) - Shutdown file watcher when stopping identity watcher\n\n* github.com/openziti/metrics: [v1.4.2 -> v1.4.5](https://github.com/openziti/metrics/compare/v1.4.2...v1.4.5)\n    * [Issue #58](https://github.com/openziti/metrics/issues/58) - Add GaugeFloat64 support\n    * [Issue #56](https://github.com/openziti/metrics/issues/56) - underlying resources of reference counted meters are not cleaned up when reference count hits zero\n\n* github.com/openziti/runzmd: [v1.0.80 -> v1.0.90](https://github.com/openziti/runzmd/compare/v1.0.80...v1.0.90)\n* github.com/openziti/sdk-golang: [v1.2.3 -> v1.7.0](https://github.com/openziti/sdk-golang/compare/v1.2.3...v1.7.0)\n    * [Issue #901](https://github.com/openziti/sdk-golang/issues/901) - Move xgress back to having retransmitter goroutine per-xgress\n    * [Issue #906](https://github.com/openziti/sdk-golang/issues/906) - Fix potential nil references on session service structs\n    * [Issue #897](https://github.com/openziti/sdk-golang/issues/897) - Allow xgress to use pull model for reads when appropriate\n    * [Issue #895](https://github.com/openziti/sdk-golang/issues/895) - Limit effect sudden rtt spikes can have on rtt moving average\n    * [Issue #902](https://github.com/openziti/sdk-golang/issues/902) - Inspect response message content types are mixed up\n    * [Issue #887](https://github.com/openziti/sdk-golang/issues/887) - Fix listener manager cleanup\n    * [Issue #886](https://github.com/openziti/sdk-golang/issues/886) - When controller is busy during service refresh, backoff and retry instead of falling back to full refresh\n    * [Issue #885](https://github.com/openziti/sdk-golang/issues/885) - Only compare relevant service fields when looking for changes\n    * [Issue #884](https://github.com/openziti/sdk-golang/issues/884) - Add deadline for bind establishment\n    * [Issue #883](https://github.com/openziti/sdk-golang/issues/883) - Router level listener can be left open if multi-listener closes during listener establishment\n    * [Issue #877](https://github.com/openziti/sdk-golang/issues/877) - Handle differences in xgress eof/end-of-circuit handling by adding a capabilities exchange\n    * [Issue #832](https://github.com/openziti/sdk-golang/issues/832) - Fuzz session refresh timers\n    * [Issue #879](https://github.com/openziti/sdk-golang/issues/879) - Return the connId in inspect response\n    * [Issue #878](https://github.com/openziti/sdk-golang/issues/878) - Fix responses from rx goroutines\n    * [Issue #874](https://github.com/openziti/sdk-golang/issues/874) - Add inspect support at the context level\n    * [Issue #871](https://github.com/openziti/sdk-golang/issues/871) - Make SDK better at sticking to MaxTerminator terminators\n    * [Issue #708](https://github.com/openziti/sdk-golang/issues/708) - Support for Go's built-in context in Dial methods\n    * [Issue #860](https://github.com/openziti/sdk-golang/issues/860) - Make the dialing identity's id and name available on dialed connections\n    * [Issue #857](https://github.com/openziti/sdk-golang/issues/857) - Use new error code and retry hints to correctly react to terminator errors\n    * [Issue #847](https://github.com/openziti/sdk-golang/issues/847) - Ensure the initial version check succeeds, to ensure we don't legacy sessions on ha or oidc-enabled controllers\n    * [Issue #824](https://github.com/openziti/sdk-golang/pull/824) - release notes and hard errors on no TOTP handler breaks partial auth events\n    * [Issue #818](https://github.com/openziti/sdk-golang/issues/818) - Full re-auth should not clear services list, as that breaks the on-change logic\n    * [Issue #817](https://github.com/openziti/sdk-golang/issues/817) - goroutines can get stuck when iterating over randomized HA controller list\n    * [Issue #736](https://github.com/openziti/sdk-golang/issues/736) - Migrate from github.com/mailru/easyjson\n    * [Issue #813](https://github.com/openziti/sdk-golang/issues/813) - SDK doesn't stop close listener when it detects that a service being hosted gets deleted\n    * [Issue #811](https://github.com/openziti/sdk-golang/issues/811) - Credentials are lost when explicitly set\n    * [Issue #807](https://github.com/openziti/sdk-golang/issues/807) - Don't send close from rxer to avoid blocking\n    * [Issue #800](https://github.com/openziti/sdk-golang/issues/800) - Tidy create service session logging\n\n* github.com/openziti/secretstream: [v0.1.39 -> v0.1.49](https://github.com/openziti/secretstream/compare/v0.1.39...v0.1.49)\n* github.com/openziti/transport/v2: [v2.0.188 -> v2.0.215](https://github.com/openziti/transport/compare/v2.0.188...v2.0.215)\n    * [Issue #31](https://github.com/openziti/transport/issues/31) - ipv6 Transport Address Parsing\n    * [Issue #149](https://github.com/openziti/transport/issues/149) - Archive transwarp code\n\n* github.com/openziti/xweb/v3: [v2.3.4 -> v3.0.4](https://github.com/openziti/xweb/compare/v2.3.4...v3.0.4)\n    * [Issue #32](https://github.com/openziti/xweb/issues/32) - watched identities sometimes don't reload when changed\n\n* github.com/openziti/go-term-markdown: v1.0.1 (new)\n* github.com/openziti/ziti/v2: [v1.6.8 -> v2.0.0](https://github.com/openziti/ziti/compare/v1.6.8...v2.0.0)\n    * [Issue #3830](https://github.com/openziti/ziti/issues/3830) - statemanager is holding on to edge connections and they're never getting cleared\n    * [Issue #3824](https://github.com/openziti/ziti/issues/3824) - Allow calling inspect using the IPC agent on the controller, router and go tunnel\n    * [Issue #2049](https://github.com/openziti/ziti/issues/2049) - The ziti agent command should have a controller connection status\n    * [Issue #3784](https://github.com/openziti/ziti/issues/3784) - Fix link registry race condition on reporting links on reconnect\n    * [Issue #3734](https://github.com/openziti/ziti/issues/3734) - Enforce client certificate proof-of-possession on controller REST API for OIDC sessions\n    * [Issue #3806](https://github.com/openziti/ziti/issues/3806) - Expose OpenZiti-specific login and MFA endpoints in the OIDC discovery document\n    * [Issue #3818](https://github.com/openziti/ziti/issues/3818) - Filtering policies by keywords `Dial` and `Bind` doesn't work\n    * [Issue #3816](https://github.com/openziti/ziti/issues/3816) - Support multiple upstream DNS providers in ziti tunnel and ER/T\n    * [Issue #3699](https://github.com/openziti/ziti/issues/3699) - Consolidate CLI edge and fabric commands in top level create/update/delete/list/login commands\n    * [Issue #3788](https://github.com/openziti/ziti/issues/3788) - OIDC Endpoints return 400 Bad Request instead of underlying error\n    * [Issue #3680](https://github.com/openziti/ziti/issues/3680) - adds revocation control to CLI and Management API\n    * [Issue #3717](https://github.com/openziti/ziti/issues/3717) - Generic error message for specific error\n    * [Issue #3543](https://github.com/openziti/ziti/issues/3543) - New Circuit Failure code for sockets not available\n    * [Issue #3364](https://github.com/openziti/ziti/issues/3364) - Make no such host error specific\n    * [Issue #2888](https://github.com/openziti/ziti/issues/2888) - New specific Error code for port not allowed\n    * [Issue #2859](https://github.com/openziti/ziti/issues/2859) - Create specific error code for DNS failed resolution\n    * [Issue #1580](https://github.com/openziti/ziti/issues/1580) - Invalid link destination should have a specific error code\n    * [Issue #3706](https://github.com/openziti/ziti/issues/3706) - Increase link payload/ack queue sizes and make them configurable\n    * [Issue #3778](https://github.com/openziti/ziti/issues/3778) - SetRouterDataModel can deadlock in the router\n    * [Issue #3777](https://github.com/openziti/ziti/issues/3777) - With the new circuit reserve, we can have circuits with no path in the controller circuit set, which can cause panics\n    * [Issue #3770](https://github.com/openziti/ziti/issues/3770) - Update Token Requests Should Close Channel Connections If Invalid\n    * [Issue #3762](https://github.com/openziti/ziti/issues/3762) - Revocations not included in full router data model state\n    * [Issue #3757](https://github.com/openziti/ziti/issues/3757) - Mesh peer signing cert from header is overwritten by TLS underlay cert\n    * [Issue #3756](https://github.com/openziti/ziti/issues/3756) - TLS handshake rate limiter timeout check reads from wrong config scope\n    * [Issue #3755](https://github.com/openziti/ziti/issues/3755) - commandHandler config read from wrong scope\n    * [Issue #3754](https://github.com/openziti/ziti/issues/3754) - dialFailed drops applyFailed parameter, preventing duplicate link retry jitter\n    * [Issue #3753](https://github.com/openziti/ziti/issues/3753) - SPIFFE trust domain prefix check has swapped HasPrefix arguments\n    * [Issue #3746](https://github.com/openziti/ziti/issues/3746) - The controller connect events control channel handler leaks a goroutine\n    * [Issue #3747](https://github.com/openziti/ziti/issues/3747) - Update controller peer error marshalling for app code changes\n    * [Issue #3721](https://github.com/openziti/ziti/issues/3721) - Add CreateCircuitV3 to controller\n    * [Issue #3719](https://github.com/openziti/ziti/issues/3719) - Allow binding specific inspects to pass through to xgress listener implementations\n    * [Issue #3696](https://github.com/openziti/ziti/issues/3696) - oidc provider is non-deterministic for wildcard certs\n    * [Issue #3681](https://github.com/openziti/ziti/issues/3681) - coalesce OIDC JWT revocations to reduce controller write pressure\n    * [Issue #3683](https://github.com/openziti/ziti/issues/3683) - add fablab test for testing flow control changes over a longer term\n    * [Issue #3673](https://github.com/openziti/ziti/issues/3673) - revocation build-up in db and rdm\n    * [Issue #3674](https://github.com/openziti/ziti/issues/3674) - Update to Go 1.26\n    * [Issue #3496](https://github.com/openziti/ziti/issues/3496) - MFA TOTP Enrollment During OIDC Authentication Does Not Work\n    * [Issue #3609](https://github.com/openziti/ziti/issues/3609) - Stabilize terminator creation test for 2.0\n    * [Issue #3648](https://github.com/openziti/ziti/issues/3648) - tunnel: myCopy logs router ID as circuitId, causing misleading debug output\n    * [Issue #3658](https://github.com/openziti/ziti/issues/3658) - Raft cluster join fails with \"hello message too big\" when using long hostnames\n    * [Issue #3626](https://github.com/openziti/ziti/issues/3626) - controller: overlay bind point produces malformed URL in /versions apiBaseUrls\n    * [Issue #3635](https://github.com/openziti/ziti/issues/3635) - Allow controllers to dial routers to support more topologies\n    * [Issue #3607](https://github.com/openziti/ziti/issues/3607) - linux installer not upgradable from v1\n    * [Issue #3650](https://github.com/openziti/ziti/issues/3650) - Reroute doesn't proactively clean up orphaned route entries\n    * [Issue #3571](https://github.com/openziti/ziti/issues/3571) - Ensure 2.0 backwards compatibility with 1.6 and 1.5 using the smoketest\n    * [Issue #3636](https://github.com/openziti/ziti/issues/3636) - Adaptive rate limiter should use success rate rather than queue position\n    * [Issue #3600](https://github.com/openziti/ziti/issues/3600) - Add a preferredLeader flag, allow selected nodes to be preferred for raft leader, if they're available\n    * [Issue #3642](https://github.com/openziti/ziti/issues/3642) - Use xgress_common.Connection type for xgress_transport and xgress_proxy\n    * [Issue #3597](https://github.com/openziti/ziti/issues/3597) - Enable OIDC API by default\n    * [Issue #3624](https://github.com/openziti/ziti/issues/3624) - Multi-underlay control channel doesn't correctly handle lack of group secret on non-grouped underlays\n    * [Issue #3613](https://github.com/openziti/ziti/issues/3613) - Initializing cluster from existing db using `db:` config settings results in panic\n    * [Issue #3620](https://github.com/openziti/ziti/issues/3620) - Controller control channel heartbeats config parsing was erroneously nested under options parsing\n    * [Issue #3619](https://github.com/openziti/ziti/issues/3619) - Router connect events full sync interval was using min/max values meant for the batch interval\n    * [Issue #3618](https://github.com/openziti/ziti/issues/3618) - Router interfaceDiscovery.minReportInterval value was being set to checkInterval\n    * [Issue #3617](https://github.com/openziti/ziti/issues/3617) - Transit router disabled flag not passed through raft command structure\n    * [Issue #3333](https://github.com/openziti/ziti/issues/3333) - Updb user-lockout triggered by successful login attempts\n    * [Issue #3599](https://github.com/openziti/ziti/issues/3599) - Add gap detection and handling to router data model\n    * [Issue #3356](https://github.com/openziti/ziti/issues/3356) - Add WWW-Authenticate Headers\n    * [Issue #3074](https://github.com/openziti/ziti/issues/3074) - Dynamic cost range is too limited\n    * [Issue #3558](https://github.com/openziti/ziti/issues/3558) - terminator cost increased on egress dial success, not on circuit completion\n    * [Issue #3556](https://github.com/openziti/ziti/issues/3556) - global circuit costs not cleared when terminator is deleted\n    * [Issue #3557](https://github.com/openziti/ziti/issues/3557) - costing calculation for the weighted terminator selection strategy  is incorrect\n    * [Issue #2512](https://github.com/openziti/ziti/issues/2512) - Return circuit ID and error in dial failures\n    * [Issue #3569](https://github.com/openziti/ziti/issues/3569) - Version 2.0+ routers should not connect to controllers which do not support JWT formatted legacy sessions\n    * [Issue #3565](https://github.com/openziti/ziti/issues/3565) - Link dialer save 'is first conn' true, so all dials claim to be first, causing potential race condition\n    * [Issue #3575](https://github.com/openziti/ziti/issues/3575) - OIDC token endpoint code bugs possibly resulting in panics/eof errors\n    * [Issue #3550](https://github.com/openziti/ziti/issues/3550) - Support multi-underlay control channels\n    * [Issue #3535](https://github.com/openziti/ziti/issues/3535) - Remove the legacy xgress_edge_tunnel implementation\n    * [Issue #3547](https://github.com/openziti/ziti/issues/3547) - Add support for sending the dialing identity id and name to the hosting sdk\n    * [Issue #3541](https://github.com/openziti/ziti/issues/3541) - Remove option to disable the router data model in the controller\n    * [Issue #3540](https://github.com/openziti/ziti/issues/3540) - Handle UDP difference between proxy and tproxy implementations\n    * [Issue #3527](https://github.com/openziti/ziti/issues/3527) - ER/T UDP tunnels keep closed connections for 30s, preventing potential new good connections in that time\n    * [Issue #3526](https://github.com/openziti/ziti/issues/3526) - ER/T half-close logic is incorrect\n    * [Issue #3524](https://github.com/openziti/ziti/issues/3524) - Provide more error context to SDKs for terminator errors\n    * [Issue #3509](https://github.com/openziti/ziti/issues/3509) - Enforce policy on the router for oidc sessions, by closing open circuits and terminators when service access is lost\n    * [Issue #3531](https://github.com/openziti/ziti/issues/3531) - Remove created/updated/deleted terminator events. Obsoleted by entity change events.\n    * [Issue #3532](https://github.com/openziti/ziti/issues/3532) - Removed deprecated create identity <type> subcommands\n    * [Issue #3521](https://github.com/openziti/ziti/issues/3521) - Cleanup CLI to remove calls to os.Exit to be embed friendlier\n    * [Issue #3516](https://github.com/openziti/ziti/issues/3516) - Remove support for create terminator v1\n    * [Issue #3512](https://github.com/openziti/ziti/issues/3512) - Remove legacy link management code from the controller\n    * [Issue #3511](https://github.com/openziti/ziti/issues/3511) - router proxy mode fails to resolve interface if binding is 0.0.0.0\n    * [Issue #3503](https://github.com/openziti/ziti/issues/3503) - Allow routers to request current cluster membership information\n    * [Issue #3501](https://github.com/openziti/ziti/issues/3501) - Get cluster membership information from raft directly, rather than trying to cache it in the DB\n    * [Issue #3500](https://github.com/openziti/ziti/issues/3500) - Set a router data model timeline when initializing a new HA setup, rather than letting it stay blank\n    * [Issue #3504](https://github.com/openziti/ziti/issues/3504) - Reduce router data model full state updates\n    * [Issue #3492](https://github.com/openziti/ziti/pull/3492) - Bump openziti/ziti-console-assets from 3.12.9 to 4.0.0 in /dist/docker-images/ziti-controller in the all group\n    * [Issue #3484](https://github.com/openziti/ziti/issues/3484) - router ctrl channel handler for handling cluster changes has an initialization race condition\n    * [Issue #3477](https://github.com/openziti/ziti/issues/3477) - Optionally enable model changes triggered by login to be non-blocking and to be droppable if the system is under load\n    * [Issue #3473](https://github.com/openziti/ziti/issues/3473) - Enable tls handshake rate limiter by default and tweak default values.\n    * [Issue #3471](https://github.com/openziti/ziti/issues/3471) - Go tunneler is ignoring host config MaxConnections\n    * [Issue #3469](https://github.com/openziti/ziti/issues/3469) - Only send model updates on resubscribe if the RDM index has advanced\n    * [Issue #2573](https://github.com/openziti/ziti/issues/2573) - An edge router in a tight restart loop causes a resource leak on routers to which it connects.\n    * [Issue #3430](https://github.com/openziti/ziti/issues/3430) - Add permissions list to identity\n    * [Issue #2109](https://github.com/openziti/ziti/issues/2109) - Add Edge Management Read Only Capability\n    * [Issue #3435](https://github.com/openziti/ziti/issues/3435) - Add edge management API permissions by entity type and action\n    * [Issue #3441](https://github.com/openziti/ziti/issues/3441) - Update router connection tracker to interrogate active connections\n    * [Issue #3451](https://github.com/openziti/ziti/issues/3451) - ci - compare only stable releases when promoting\n    * [Issue #3437](https://github.com/openziti/ziti/issues/3437) - SDK OIDC token updates to routers should return an error if invalid\n    * [Issue #3348](https://github.com/openziti/ziti/issues/3348) - Unable to clear/reset the \"tags\" property on an entity to an empty object\n    * [Issue #3452](https://github.com/openziti/ziti/issues/3452) - `ziti agent cluster add` has bad behavior if the add address doesn't match the advertise address\n    * [Issue #3410](https://github.com/openziti/ziti/issues/3410) - Consolidate fabric REST API code with edge management and edge client code\n    * [Issue #3425](https://github.com/openziti/ziti/issues/3425) - RDM not properly responding to tunneler enabled flag changes\n    * [Issue #3420](https://github.com/openziti/ziti/issues/3420) - The terminator id cache uses the same id for all terminators in a host.v2 config, resulting in a single terminator\n    * [Issue #3419](https://github.com/openziti/ziti/issues/3419) - When using the router data model, precedence specified on the per-service identity mapping are incorrectly interpreted\n    * [Issue #3318](https://github.com/openziti/ziti/issues/3318) - Terminator creation seems to slow exponentially as the number of terminators rises from 10k to 20k to 40k\n    * [Issue #3407](https://github.com/openziti/ziti/issues/3407) - The CLI doesn't properly pass JWT authentication information to websocket endpoints\n    * [Issue #3359](https://github.com/openziti/ziti/issues/3359) - Ensure router data model subscriptions have reasonable performance and will scale\n    * [Issue #3354](https://github.com/openziti/ziti/issues/3354) - SDK/ENV Info from SDKs is not distributed in HA\n    * [Issue #3381](https://github.com/openziti/ziti/issues/3381) - the fabric service REST apis are missing the maxIdleTime property\n    * [Issue #3382](https://github.com/openziti/ziti/issues/3382) - Legacy service sessions generated pre-1.7.x are incompatible with v1.7.+ and need to be cleared\n    * [Issue #3339](https://github.com/openziti/ziti/issues/3339) - get router ctrl.endpoint from ctrls claim in JWT\n    * [Issue #3378](https://github.com/openziti/ziti/issues/3378) - login with file stopped working\n    * [Issue #3349](https://github.com/openziti/ziti/issues/3349) - UPDB OIDC login returns wrong content type\n    * [Issue #2324](https://github.com/openziti/ziti/issues/2324) - Add Ext-JWT/OIDC enrollment\n    * [Issue #3346](https://github.com/openziti/ziti/issues/3346) - Fix confusing attempt logging\n    * [Issue #3337](https://github.com/openziti/ziti/issues/3337) - Router reports \"no xgress edge forwarder for circuit\"\n    * [Issue #3345](https://github.com/openziti/ziti/issues/3345) - Clean up connect events tests and remove global XG registry\n    * [Issue #3264](https://github.com/openziti/ziti/issues/3264) - Allow routers to generate alert events in cases of service misconfiguration\n    * [Issue #3321](https://github.com/openziti/ziti/issues/3321) - Health Check API missing base path on discovery endpoint\n    * [Issue #3323](https://github.com/openziti/ziti/issues/3323) - router/tunnel static services fail to bind unless new param protocol is defined\n    * [Issue #3309](https://github.com/openziti/ziti/issues/3309) - Detect link connections meant for another router\n    * [Issue #3286](https://github.com/openziti/ziti/issues/3286) - edge-api binding doesn't have the correct path on discovery endpoints\n    * [Issue #3297](https://github.com/openziti/ziti/issues/3297) - stop promoting hotfixes downstream\n    * [Issue #3295](https://github.com/openziti/ziti/issues/3295) - make ziti tunnel service:port pairs optional\n    * [Issue #3291](https://github.com/openziti/ziti/issues/3291) - replace decommissioned bitnami/kubectl\n    * [Issue #3277](https://github.com/openziti/ziti/issues/3277) - Router can deadlock on closing a connection if the incoming data channel is full\n    * [Issue #3269](https://github.com/openziti/ziti/issues/3269) - Add host-interfaces config type\n    * [Issue #3258](https://github.com/openziti/ziti/issues/3258) - Add config type proxy.v1 so proxies can be defined dynamically for the ER/T\n    * [Issue #3259](https://github.com/openziti/ziti/issues/3259) - Interfaces config type not added due to wrong name\n    * [Issue #3265](https://github.com/openziti/ziti/issues/3265) - Forwarding errors should log at debug, since they are usual part of circuit teardown\n    * [Issue #3261](https://github.com/openziti/ziti/issues/3261) - ER/T dialed xgress connections may only half-close when peer is fully closed\n\n\n","mentions_count":3},{"url":"https://api.github.com/repos/openziti/ziti/releases/311377028","assets_url":"https://api.github.com/repos/openziti/ziti/releases/311377028/assets","upload_url":"https://uploads.github.com/repos/openziti/ziti/releases/311377028/assets{?name,label}","html_url":"https://github.com/openziti/ziti/releases/tag/v2.0.0-pre11","id":311377028,"author":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"node_id":"RE_kwDODVFMN84SjzyE","tag_name":"v2.0.0-pre11","target_commitish":"main","name":"v2.0.0-pre11","draft":false,"immutable":false,"prerelease":true,"created_at":"2026-04-20T19:13:00Z","updated_at":"2026-04-20T19:17:07Z","published_at":"2026-04-20T19:17:07Z","assets":[{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/400968948","id":400968948,"node_id":"RA_kwDODVFMN84X5kz0","name":"checksums.sha256.txt","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"text/plain; charset=utf-8","state":"uploaded","size":798,"digest":"sha256:135ccd8fe521b35b63c0d00fca84a4d75b98fa52b220f727441697a5051c7b7b","download_count":6,"created_at":"2026-04-20T19:17:04Z","updated_at":"2026-04-20T19:17:04Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre11/checksums.sha256.txt"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/400968946","id":400968946,"node_id":"RA_kwDODVFMN84X5kzy","name":"sbom-v2.0.0-pre11.spdx.json","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/json","state":"uploaded","size":1003139,"digest":"sha256:a6f5f83f66963c2928eb05ef021a81391be844def304aff58417ca7b27d7cd2c","download_count":3,"created_at":"2026-04-20T19:17:04Z","updated_at":"2026-04-20T19:17:04Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre11/sbom-v2.0.0-pre11.spdx.json"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/400968947","id":400968947,"node_id":"RA_kwDODVFMN84X5kzz","name":"source-v2.0.0-pre11.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":3829520,"digest":"sha256:1a0771a8f7d28cf7c4cc350209599b6c1068369feb56d0271d5d8e0813491913","download_count":5,"created_at":"2026-04-20T19:17:04Z","updated_at":"2026-04-20T19:17:04Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre11/source-v2.0.0-pre11.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/400968944","id":400968944,"node_id":"RA_kwDODVFMN84X5kzw","name":"ziti-darwin-amd64-2.0.0-pre11.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":54411783,"digest":"sha256:59933673ec83f48d424228767b42ee3852280209b48a1e88f8fc089f7cfe3fd6","download_count":10,"created_at":"2026-04-20T19:17:04Z","updated_at":"2026-04-20T19:17:07Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre11/ziti-darwin-amd64-2.0.0-pre11.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/400968908","id":400968908,"node_id":"RA_kwDODVFMN84X5kzM","name":"ziti-darwin-arm64-2.0.0-pre11.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":50746563,"digest":"sha256:e632af316bdf6d32d8ae5621e229051560e5364c52187a35c5f6752941b8debb","download_count":6,"created_at":"2026-04-20T19:17:01Z","updated_at":"2026-04-20T19:17:04Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre11/ziti-darwin-arm64-2.0.0-pre11.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/400968906","id":400968906,"node_id":"RA_kwDODVFMN84X5kzK","name":"ziti-linux-amd64-2.0.0-pre11.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":55744800,"digest":"sha256:d805ecb46f514223f32db0fad3ed5cc1a2653029aa747ee115406d368c36b87d","download_count":93,"created_at":"2026-04-20T19:17:01Z","updated_at":"2026-04-20T19:17:04Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre11/ziti-linux-amd64-2.0.0-pre11.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/400968907","id":400968907,"node_id":"RA_kwDODVFMN84X5kzL","name":"ziti-linux-arm-2.0.0-pre11.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":52196227,"digest":"sha256:4871ff3c7a19fbb712f9f92cd5cedf014e365c14e72f8b769149d6bd2fc488a6","download_count":7,"created_at":"2026-04-20T19:17:01Z","updated_at":"2026-04-20T19:17:04Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre11/ziti-linux-arm-2.0.0-pre11.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/400968910","id":400968910,"node_id":"RA_kwDODVFMN84X5kzO","name":"ziti-linux-arm64-2.0.0-pre11.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":52213505,"digest":"sha256:7c57c99be3521db0f06bf30ce30da45b7603964864f9b135c092b81b0cc1b313","download_count":14,"created_at":"2026-04-20T19:17:01Z","updated_at":"2026-04-20T19:17:04Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre11/ziti-linux-arm64-2.0.0-pre11.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/400968905","id":400968905,"node_id":"RA_kwDODVFMN84X5kzJ","name":"ziti-windows-amd64-2.0.0-pre11.zip","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/zip","state":"uploaded","size":45063148,"digest":"sha256:ae697a38d82b5e2c40036eceaf5843bb7e58d297244c1e961792f08d548f1e17","download_count":13,"created_at":"2026-04-20T19:17:01Z","updated_at":"2026-04-20T19:17:04Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre11/ziti-windows-amd64-2.0.0-pre11.zip"}],"tarball_url":"https://api.github.com/repos/openziti/ziti/tarball/v2.0.0-pre11","zipball_url":"https://api.github.com/repos/openziti/ziti/zipball/v2.0.0-pre11","body":"# Release 2.0.0\n\n## What's New\n\nThis is the next major version release of OpenZiti, following the 1.0 release in April 2024.\nOf particular note is that HA controllers are now considered ready for general use.\nThis release also introduces a new permissions model, OIDC/JWT token-based enrollment, \nclustering performance improvements, and a number of other features and fixes. Because \nsome of these changes are not backwards compatible with older routers, we're marking this \nas a major version bump.\n\n### HA Controllers are now considered ready for general use\n\nThis is a pretty big milestone and marks the completion of work that's been ongoing for a couple of years.\nThe HA work has brought with it some notable changes. Authentication now uses JWTs by default. Using JWTs\nmeans the controllers don't need to store session and propagate them to the routers. This removes a bottleneck\nfrom the network and allows the load to be more easily distributed among controllers and routers.\n\nTo support distributed authentication, the routers now get a bespoke version of the data model. In addition\nto enabling distributed authentication this will allow us to remove the need for service polling and further \nreduce the load on controllers in the future.\n\n### Router Compatibility\n\nRelated to the JWT work, routers with version 2.+ will only work with controllers that are version 2.+. This means\nto upgrade your network, controllers should be upgraded first. Routers can then be upgraded individually.\n\n2.x routers should still work fine with older router versions.\n\nWe try very hard to avoid breaking changes like this, but sometimes the engineering trade-offs lead there. This change\nwas first made in the 1.7 release. That release has not been marked stable, and we have no plans to do so, because\nof the backwards incompatibility. \n\nIf you need to support in the 1.6.12+ range, see the section \"OIDC enabled by default\".\n\n### New Permissions Model (BETA)\n\nAs one feature goes out of beta, another arrives into beta. This release introduces a new permissions system\nfor more fine grained control to the management API. It's not expected to change, but may do so based on feedback\nfrom users.\n\n### CLI Reorganization\n\nThe `ziti` CLI has been reorganized to consolidate commands that were previously \nspread across `ziti edge` and `ziti fabric` into unified top-level commands. Entity \nmanagement is now available directly via `ziti create`, `ziti delete`, `ziti list`, \nand `ziti update`. Session management has been simplified with top-level `ziti login`. \nThe existing `ziti edge` and `ziti fabric` command trees remain available.\n\n**Breaking change:** `ziti create ca` now creates a Ziti edge Certificate Authority \n(previously it created a PKI CA). PKI CA creation is still available via `ziti pki create ca`.\n\nSee [CLI Reorganization Details](#cli-reorganization-details) for the full command mapping.\n\n### Updated Release Process\n\nWe have moved to creating `-preN` releases for major and minor versions. This way we can put out release candidates,\nor feature previews and put them through internal testing and let interested folks from the community try them out.\nThen, when we're ready, we can run the full validation suite against the last pre-release and retag it. \n\nPatch releases won't have `-preN` and should contain only high priority bug fixes.\n\n### Deprecation Cleanup\n\nSince we already have a breaking change, we're removing some other backwards compatibility code.\n\n* Controller managed links \n    * Router managed links were introduced in v0.30.0. \n    * If you're upgrading from an older versions, you'll want to upgrade to the latest 1.x release before jumping to 2.x\n    * Github tracking issue: https://github.com/openziti/ziti/issues/3512\n* `ziti edge create identity <type>`\n    * Identity types other than router were removed in v0.30.2\n    * The `type` can be dropped from the CLI command\n    * Github tracking issue: https://github.com/openziti/ziti/issues/3532\n* Terminator create/update/delete events\n    * These have been superseded by entity change events, which also have create/update/delete events for terminators\n    * Entity change events were introduced in v0.28.0\n    * Github tracking issue: https://github.com/openziti/ziti/issues/3531\n* `xgress_edge_tunnel` v1\n    * This is the first implementation of the tunneler in edge-router code (ER/T) which used legacy api sessions and services\n    * The v2 version uses the router data model and was introduced in v0.30.x\n    * Github tracking issue: https://github.com/openziti/ziti/issues/3516\n\n### Legacy Session Deprecation\n\nOIDC sessions are now preferred. They are the default, or will become the default for SDKs and tunnelers. They are also required\nwhen running HA. Legacy API and service session are now deprecated and will be removed in the OpenZiti v3.0.0 release. \n\n### Additional Features\n\n* Controllers can now optionally bind APIs using an OpenZiti identity\n* `ziti edge login` now supports the `--network-identity` flag to authenticate and establish connections through the Ziti overlay network\n* `ziti edge login` now supports using a bearer token with `--token` for authentication. The token is expected to be\n  provided as just the JWT, not with the \"Bearer \" prefix\n* Identity configuration can now be loaded from files or environment variables for flexible deployment scenarios\n* Identities can now be provisioned just-in-time through OIDC/JWT token-based enrollment\n* Multiple model updates can now be in-flight at the same time, improving clustering performance\n* Authentication-related model updates can now be non-blocking and even dropped if the system is too busy\n* Routers now provide more error context to SDKs for terminator errors, enabling better retry behavior\n* New `proxy.v1` config type for dynamic service proxies (originally released in 1.7.0)\n* New alert event type for surfacing operational issues to network operators - Beta (originally released in 1.7.0)\n* New Azure Service Bus event sink for streaming controller events, contributed by @ffaraone (originally released in 1.7.0)\n* Bundled ZAC upgraded to 4.0\n* Build updated to Go 1.25\n* CLI cleaned up to remove calls to `os.Exit`, making it more friendly for embedding\n* OIDC is now enabled by default\n* Controller Edge APIs now return `WWW-Authenticate` response headers on `401 Unauthorized` responses, giving clients actionable information about which auth methods are accepted and what went wrong\n* HA Controllers can be marked as 'preferredLeader' via config\n* Dynamic cost range for smart routing expanded beyond the previous 64K limit\n* Dial failures now return the circuit ID and error information for easier debugging\n* Router-to-controller control channels now support multiple underlays with priority-based message routing\n* The dialing identity's ID and name are now forwarded to the hosting SDK\n* Controllers can now dial routers to establish control channels, enabling connectivity when routers are behind firewalls (Beta)\n* Refresh-token revocations are now batched and best-effort, removing the database/raft bottleneck on token refreshes\n* `ziti edge quickstart` now always runs in HA mode. The `ha` subcommand has been removed. Use\n  `ziti edge quickstart join` to add additional members to the cluster. Note: existing quickstart instances\n  are not compatible with the new HA-only mode and will need to be recreated.\n* The `--clustered` flag on `ziti create config controller` has been removed; the generated config is always\n  cluster-ready. If you have scripts passing `--clustered`, remove it.\n* [Connect events pool](#connect-events-pool) - fixes a goroutine leak when routers reconnect\n* [Multiple DNS upstreams](#multiple-dns-upstreams) - the tunneler can now fan out recursive queries to several upstream resolvers in parallel\n\n## Connect Events Pool\n\nThe controller now uses a shared, bounded goroutine pool to process identity\nconnect/disconnect events from routers. Previously each router connection spawned\na dedicated goroutine that was never cleaned up on disconnect, leaking a goroutine\nper reconnect cycle. Under churn (e.g., chaos testing with hundreds of routers) this\ncould accumulate tens of thousands of leaked goroutines and destabilize the controller.\n\nThe pool is configurable in the controller config file:\n\n```yaml\nconnectEvents:\n  queueSize:  16    # work queue depth (default: 16)\n  minWorkers: 0     # minimum pool goroutines (default: 0)\n  maxWorkers: 16    # maximum pool goroutines (default: 16)\n  idleTime:   30s   # worker idle timeout before exit (default: 30s)\n```\n\nThe defaults are suitable for most deployments. Workers scale up on demand and\nexit after the idle timeout, so no goroutines are held when there is no work.\n\n## Community Contributors\n\nThank you to the following community members for their contributions:\n\n* @ffaraone - Azure Service Bus event sink\n* @dmuensterer - OIDC token refresh fixes\n* @nenkoru - Controller isleader health check endpoint\n* Jan Starkl - UPDB auth attempts fix\n* Mamy Ratsimbazafy - uint16 port range fix\n\n## Basic Permission System (BETA)\n\nAdded a basic permission system that allows more control over identity access to controller management API operations. \nThis replaces the previous binary admin/non-admin model with a more flexible permission system.\n\n**NOTE:** This feature is in BETA, primarily so we can get feedback on which permissions make sense. The implementation is unlikely to change\nbut the set of exposed permissions may grow, shrink or change based on user feedback. \n\n### Permission Model\n\nThe permission system supports three levels of authorization:\n\n  1. **Global Permissions**: System-wide access levels\n     - `admin` - Full access to all operations. This is still controlled by the `isAdmin` flag on identity\n     - `admin_readonly` - Read-only access to all resources except debugging facilities inspect and validate\n\n  2. **Entity-Level Permissions**: Full CRUD access to specific entity types\n     - Granting an entity-level permission (e.g., `service`) provides complete create, read, update, and delete access for that entity type\n\n  3. **Action-Level Permissions**: Specific operation access on entity types\n     - Fine-grained control using the pattern `<entity>.<action>` (e.g., `service.read`, `identity.update`)\n     - Supports `create`, `read`, `update`, and `delete` actions per entity type\n\n### Supported Entity Permissions\n\nThe following entity-level permissions are available:\n\n- `auth-policy` - Authentication policy management\n- `ca` - Certificate Authority management\n- `config` - Configuration management\n- `config-type` - Configuration type management\n- `edge-router-policy` - Edge router policy management\n- `enrollment` - Enrollment management\n- `external-jwt-signer` - External JWT signer management\n- `identity` - Identity management\n- `posture-check` - Posture check management\n- `router` - Edge and transit router management\n- `service` - Service management\n- `service-policy` - Service policy management\n- `service-edge-router-policy` - Service edge router policy management\n- `terminator` - Terminator management\n- `ops` - Operational resources (API sessions, sessions, circuits, links, inspect and validate)\n\n### Permission Assignment\n\nPermissions are assigned to identities via the `permissions` field in the identity resource. Multiple permissions can be granted to a single identity, and permissions are additive.\n\n### Cross-Entity Operations\n\nListing related entities through an entity's endpoints requires appropriate permissions for the related entity type. For example:\n- Listing services for a service-policy requires `service.read` permission\n- Listing identities for an edge-router-policy requires `identity.read` permission\n- Listing configs for a service requires `config.read` permission\n\n**NOTE:** \nMore permissions than expected may be required when performing actions through the CLI or ZAC. Take for example, when an identity \nhas `config.create` and is attempting to create a new config. The CLI may fail if the identity doesn't have `config-type.read`\nas well because it will need to look up the config type id that corresponds to the given config type name.\n\nSimilar cross entity read permissions may be required when creating services.\n\n### Admin Protection\n\nNon-admin identities cannot:\n- Create identities with the `isAdmin` flag\n- Create identities with any permissions granted\n- Modify admin-related fields on existing identities\n- Update or delete admin identities\n- Grant permissions to identities\n\nThese protections ensure that privilege escalation is prevented and admin access remains controlled.\n\n\n## Binding Controller APIs With Identity\n\nController APIs can now be bound to an OpenZiti overlay network identity, allowing secure communication through\nthe Ziti network. This is useful for scenarios where you want to expose controller APIs only through the overlay\nnetwork rather than on a standard network interface.\n\n### Configuration Structure\n\nA standard `bindPoint` configuration looks like this:\n```text\n    bindPoints:\n      - interface: 127.0.0.1:18441\n        address: 127.0.0.1:18441\n```\n\nTo bind controller APIs to an OpenZiti identity, add an additional `identity` block to your `bindPoints`. The\nidentity configuration specifies where to load the Ziti identity file and which service to bind it to:\n\n```text\n    bindPoints:\n      - interface: 127.0.0.1:18441\n        address: 127.0.0.1:18441\n      - identity:\n          file: \"c:/temp/ctrl.testing/ctrl.identity.json\"\n          service: \"mgmt\"\n```\n\n### Supported Configuration Options\n\n- `file`: Path to a Ziti identity JSON file containing the controller's identity and enrollment certificate\n- `env`: Name of an environment variable containing a base64-encoded Ziti identity (alternative to `file`)\n- `service`: The name of the Ziti service to bind the controller API to\n\n### Using Environment Variables\n\nFor deployments where storing identity files on disk is not preferred, you can reference a base64-encoded\nidentity file from an environment variable. The environment variable should contain the base64-encoded contents\nof the identity JSON file.\n\nFor example, if an environment variable named `ZITI_CTRL_IDENTITY` contains a base64-encoded identity file:\n\n```text\n    bindPoints:\n      - interface: 127.0.0.1:18441\n        address: 127.0.0.1:18441\n      - identity:\n          env: ZITI_CTRL_IDENTITY\n          service: \"mgmt\"\n```\n\n### IPv6 Support\n\nBoth IPv4 and IPv6 addresses are supported for standard bind points. IPv6 addresses should be specified in bracket\nnotation with a port number:\n\n```text\n    bindPoints:\n      - interface: \"[::1]:18441\"\n        address: \"[::1]:18441\"\n      - identity:\n          file: \"/path/to/identity.json\"\n          service: \"mgmt\"\n```\n\n## CLI Enhancements for Identity-Based Connections\n\nThe `ziti edge login` command and REST client utilities have been enhanced to support identity-based connections\nthrough the Ziti overlay network.\n\n### New `--network-identity` Flag for `ziti edge login`\n\nThe `ziti edge login` command now includes a `--network-identity` flag that allows you to authenticate to a Ziti\ncontroller through the overlay network using a Ziti identity:\n\n```bash\nziti edge login https://ziti.mgmt.apis.local:1280 \\\n  --username myuser \\\n  --password mypass \\\n  --network-identity /path/to/identity.json\n```\n\nThis is useful when the controller is only accessible through the Ziti overlay network or when you want to ensure\nall communication to the controller flows through the overlay for security purposes.\n\n### Identity Resolution Order\n\nWhen establishing connections, identities are resolved in the following order:\n\n1. **Command-line flag**: The `--network-identity` flag takes precedence\n2. **Environment variable**: If `ZITI_CLI_NETWORK_ID` is set and contains a base64-encoded identity, it is used\n3. **Cached identity file**: If a network identity was saved from a previous login in the Ziti config directory, it may be used\n\nThis layered approach allows for flexibility in deployment scenarios:\n- Development: Use command-line flags for quick testing\n- Automation: Use environment variables in CI/CD pipelines\n- Production: Cache identities securely for repeated access\n\n#### Dialing Modes When Authenticating\n\nThe CLI supports two dialing modes:\n\n**Intercept-based Dialing (Default)**\nBy default, URLs are expected to leverage intercepts. Create a service with an appropriate intercept config and use\nthe intercept address when dialing. This is the standard mode for most use cases. For example, given a service with\nthe intercept `ziti.mgmt.apis.local`\n```bash\nziti edge login https://ziti.mgmt.apis.local:1280 \\\n  --username myuser \\\n  --password mypass \\\n  --network-identity /path/to/identity.json\n```\n\n**Identity-aware Dialing (Addressable Terminators)**\nTo support addressable terminators-based dialing, specify a user in the URL. This activates dial-by-identity\nfunctionality. The URL format should be `identity-to-dial@service-name-to-dial`. For example:\n```bash\nziti edge login https://my-identity@my-service:1280 \\\n  --username myuser \\\n  --password mypass \\\n  --network-identity /path/to/identity.json\n```\n\nIn this mode, the transport extracts the identity from the URL and uses it to establish a direct connection to\nthe specified service via the addressable terminator.\n\n\n## OIDC/JWT Token-based Enrollment\n\nOpenZiti now supports provisioning identities just-in-time through OIDC/JWT token enrollment. External identity \nproviders can be configured to allow identities to enroll using JWT tokens, with support for the resulting \nidentities to use certificate or token authentication.\n\n### External JWT Signer Configuration\n\nExternal JWT signers are configured via the Edge Management API to define enrollment behavior with the following new \nenrollment-specific properties:\n\n- **enrollToCertEnabled** - When enabled, identities can exchange a JWT token and a certificate signing request (CSR) \n        for a client certificate during enrollment. The certificate can then be used for standard certificate-based\n        authentication.\n\n- **enrollToTokenEnabled** - When enabled, identities can use a JWT token to enroll. The current token or future tokens\n        may be used for authentication.\n\n- **enrollNameClaimsSelector** - Specifies which JWT claim contains the identity name. Accepts a JSON pointer \n        (e.g., `/preferred_username`) or a simple property name (e.g., `preferred_username`, automatically converted to\n        `/preferred_username`). Defaults to `/sub` if not specified. The extracted value becomes the identity name in Ziti.\n\n- **enrollAttributeClaimsSelector** - Specifies which JWT claims to extract as identity attributes during enrollment.\n        Accepts a JSON pointer (e.g., `/roles`) or a simple property name (e.g., `roles`). Extracted attributes are \n        applied to the newly enrolled identity for use in authorization policies.\n\n- **enrollAuthPolicyId** - Specifies the authentication policy to apply to newly enrolled identities. This determines\n        what authentication methods are available for the identity post-enrollment.\n\nAdditionally the existing property named **claimsProperty** that specifies external id to match identities to:\n\n- now supports a JSON pointer (e.g., `/id`) or a simple property name (e.g., `id`)\n- is used to populate the `externalId` field of the identity\n\n### Enrollment Paths\n\n#### Certificate Enrollment (enrollToCertEnabled)\n\nWhen certificate enrollment is enabled, unauthenticated users can:\n\n1. Obtain a list of available IdPs from the public Edge Client API `GET /external-jwt-signers` endpoint, where \n   `enrollToCertEnabled` is set to `true`\n2. Obtain a JWT from the configured OIDC provider\n3. Generate a certificate signing request (CSR)\n4. Submit an enrollment request with the JWT and CSR\n5. Have their identity created in Ziti with attributes extracted from JWT claims\n6. Receive a signed client certificate for certificate-based authentication\n\n#### Token Enrollment (enrollToTokenEnabled)\n\nWhen token enrollment is enabled, unauthenticated users can:\n\n1. Obtain a list of available IdPs from the public Edge Client API `GET /external-jwt-signers` endpoint, where \n   `enrollToTokenEnabled` is set to `true`\n1. Obtain a JWT from the configured OIDC provider\n2. Submit an enrollment request with the JWT\n3. Have their identity created in Ziti with attributes extracted from JWT claims\n4. Receive a Ziti API token for token-based authentication\n\n### Edge Management API\n\nThe Edge Management API provides full CRUD operations for configuring external JWT signers:\n\n- `POST /external-jwt-signers` - Create a new external JWT signer with all configuration options\n- `GET /external-jwt-signers` - List all configured external JWT signers\n- `GET /external-jwt-signers/{id}` - Retrieve a specific signer configuration\n- `PUT /external-jwt-signers/{id}` - Update all fields of a signer\n- `PATCH /external-jwt-signers/{id}` - Partially update a signer\n- `DELETE /external-jwt-signers/{id}` - Delete a signer\n\n### Edge Client API\n\nThe Edge Client API exposes a reduced set of external JWT signer information for unauthenticated enrollment requests:\n\n- `GET /external-jwt-signers` - List available JWT signers with enrollment capabilities\n\nThe client API response includes the following fields for each signer:\n\n- `name` - Signer name\n- `externalAuthUrl` - URL where users obtain JWT tokens\n- `clientId` - OIDC client ID\n- `scopes` - Requested OIDC scopes\n- `openIdConfigurationUrl` - OIDC discovery endpoint\n- `audience` - Expected token audience\n- `targetToken` - Token type to use (ACCESS or ID)\n- **`enrollToCertEnabled`** - Flag indicating certificate enrollment is available\n- **`enrollToTokenEnabled`** - Flag indicating token enrollment is available\n\n### CLI Commands\n\n**Create an external JWT signer with enrollment options:**\n```\nziti edge controller create ext-jwt-signer <name> <issuer> \\\n  --jwks-endpoint <url> \\\n  --audience <audience> \\\n  --enroll-to-cert \\\n  --enroll-to-token=false \\\n  --enroll-name-claims-selector preferred_username \\\n  --enroll-attr-claims-selector roles \\\n  --enroll-auth-policy <policy-id-or-name>\n```\n\n**Update enrollment options on an existing signer:**\n```\nziti edge controller update ext-jwt-signer <name|id> \\\n  --enroll-to-cert \\\n  --enroll-auth-policy <policy-id-or-name>\n```\n\n**List external JWT signers:**\n```\nziti edge controller list ext-jwt-signers\n```\n\n## Clustering Performance Improvements\n\nIn previous releases, model updates were submitted to raft one at at time. This prevented \nraft from being efficient by allowing command batching. This release allows multiple \nmodel updates to be in-flight at the same time. \n\nNew Configuration Options\n\n1. Raft Apply Timeout (cluster.applyTimeout)\n\nLocation: Controller configuration file, under the cluster section\nType: Duration\nDefault: 5s\nDescription: Timeout for applying commands to the Raft distributed log. Commands that exceed this timeout will trigger adaptive rate limiter backoff.\n\nExample:\n```\ncluster:\n  applyTimeout: 10s\n```\n\n2. Cluster Rate Limiter Configuration (cluster.rateLimiter)\n\nA new adaptive rate limiter that controls the submission of commands to the Raft cluster. Unlike the existing command rate limiter, this specifically manages in-flight Raft operations with adaptive window sizing.\n\nConfiguration Structure:\n```\ncluster:\n  rateLimiter:\n    enabled: true\n    minSize: 5\n    maxSize: 250\n    timeout: 30s\n```\n\nSub-options:\n\n  - enabled (boolean)\n    - Default: true\n    - Description: Enable/disable adaptive rate limiting for Raft command submission\n  - minSize (integer)\n    - Default: 5\n    - Minimum: 1\n    - Description: Minimum window size for concurrent in-flight Raft operations\n  - maxSize (integer)\n    - Default: 250\n    - Description: Maximum window size for concurrent in-flight Raft operations. Must be >= minSize\n  - timeout (duration)\n    - Default: 30s\n    - Description: Time after which outstanding work is assumed to have failed if not marked completed\n\n3. Restart Self on Snapshot (cluster.restartSelfOnSnapshot)\n\nLocation: Controller configuration file, under cluster section\nType: Boolean\nDefault: false\nDescription: When true, the controller will automatically restart itself when restoring a snapshot to an initialized system. When false, the controller will exit with code 0, requiring external process management to restart it.\n\nExample:\n```\ncluster:\n    restartSelfOnSnapshot: true\n```\n\n### New Metrics\n\nThe adaptive rate limiter exposes three new metrics:\n\n  1. raft.rate_limiter.queue_size (gauge)\n    - Current number of operations queued/in-flight\n  2. raft.rate_limiter.work_timer (timer)\n    - Duration of rate-limited operations\n  3. raft.rate_limiter.window_size (gauge)\n    - Current adaptive window size\n\n## Rate Limiter Algorithm Improvements\n\nThe adaptive rate limiter tracker now uses a success rate metric to decide when to grow or shrink its\nconcurrency window, instead of using raw queue position. An exponentially decaying histogram tracks the\nratio of successes to backoffs. When the success rate exceeds a configurable threshold, the window is\ngrown by a multiplicative increase factor. When it falls below the threshold, the window is shrunk by a\nmultiplicative decrease factor. The check intervals for increase and decrease are independently configurable.\n\nThis approach produces smoother, more stable window adjustments under varying load, avoiding the\nover-aggressive shrinking that could occur when queue position alone was used as the signal.\n\nThis specific rate limiter implementation is used in three places:\n* **Controller TLS handshake rate limiting** - controls the rate of incoming TLS handshakes on the controller\n* **Raft command submission** - controls the rate of commands submitted to the Raft distributed log\n* **Router control channel rate limiting** - controls the rate of requests from the router to the controller\n\nNote: this work was done in advance of enabling the TLS handshake rate limiter by default. The TLS\nhandshake rate limiter is not yet enabled by default, but can be enabled via the `tls.rateLimiter`\nconfiguration section.\n\nNew configuration options (available under each `rateLimiter` section):\n\n  - successThreshold (float)\n    - Default: 0.9\n    - Description: Success rate threshold above which the window size will be increased and below which it will be decreased\n  - increaseFactor (float)\n    - Default: 1.02\n    - Description: Multiplier applied to the current window size when growing. Must be greater than 1\n  - decreaseFactor (float)\n    - Default: 0.9\n    - Description: Multiplier applied to the current window size when shrinking. Must be between 0 and 1\n  - increaseCheckInterval (integer)\n    - Default: 10\n    - Description: Number of successes between window size increase checks\n  - decreaseCheckInterval (integer)\n    - Default: 10\n    - Description: Number of backoffs between window size decrease checks\n\n## Background Processing for Identity Updates\n\nIdentity environment and authenticator updates that occur during authentication are now processed asynchronously in the background. \nThis prevents authentication requests from blocking when the system is under load, significantly improving resilience during thundering herd scenarios.\n\nWhen the background queue fills up, updates can be dropped as they will be refreshed on the next authentication attempt. \nThis allows the system to gracefully handle load spikes without impacting authentication performance.\n\nFor now, dropping entries when the queue fills will be disabled by default, but can be enabled, see below.\n\n### Configuration\n\nA new `command.background` configuration section controls the background processing behavior:\n\n```yaml\n  command:\n    background:\n      enabled: true           # Enable background processing (default: true)\n      queueSize: 1000        # Maximum queue size (default: 1000)\n      dropWhenFull: true     # Drop updates when queue is full (default: false)\n      delayThreshold: 50ms   # The threshold for how long updates are taking before starting to background updates\n```\n\nNote that the `commandRateLimiter` configuration section may instead be specified under `command` as `rateLimiter`.\n\nExample:\n\n```yaml\n  command:\n    background:\n      enabled: true\n      queueSize: 250\n      dropWhenFull: false\n      delayThreshold: 50ms\n    rateLimiter:\n      enabled:   true\n      maxQueued: 25\n```\n\nNote that if command rate limiter configuration is specified in both locations, the settings under `command` will take \nprecedence. The standalone `commandRateLimiter` section may be deprecated in the future.\n\n### Metrics\n\nWhen background processing is enabled, the following metrics are exposed:\n\n- command.background.queue_size - Current number of queued background tasks\n- command.background.worker_count - Current number of worker goroutines\n- command.background.busy_workers - Number of workers currently processing tasks\n- command.background.work_timer - Timer tracking background task execution (includes histogram, meter, and count)\n- command.background.dropped_entries - Count of dropped updates when queue is full (only when dropWhenFull is enabled)\n\n## New proxy.v1 Config Type\n\n*Originally released in 1.7.0*\n\nAdded support for dynamic service proxies with configurable binding and protocol options.\nThis allows Edge Routers and Tunnelers to create proxy endpoints that can forward traffic for Ziti services.\n\nThis differs from intercept.v1 in that intercept.v1 will intercept traffic on specified\nIP addresses or DNS entries to forward to a service using tproxy or tun interface,\ndepending on implementation.\n\nA proxy on the other hand will just start a regular TCP/UDP listener on the configured port,\nso traffic will have to be configured for that destination.\n\nExample proxy.v1 Configuration:\n\n```\n  {\n    \"port\": 8080,\n    \"protocols\": [\"tcp\"],\n    \"binding\": \"0.0.0.0\"\n  }\n```\n\nConfiguration Properties:\n  - port (required): Port number to listen on (1-65535)\n  - protocols (required): Array of supported protocols (tcp, udp)\n  - binding (optional): Interface to bind to. For the ER/T defaults to the configured lanIF config property.\n\nThis config type is currently supported by the ER/T when running in either proxy or tproxy mode.\n\n## Alert Events (BETA)\n\n*Originally released in 1.7.0*\n\nA new alert event type has been added to allow Ziti components to emit alerts for issues that network operators can address.\nAlert events are generated when components encounter problems such as service configuration errors or resource\navailability issues.\n\nAlert events include:\n  - Alert source type and ID (currently supports routers, with controller and SDK support planned for future releases)\n  - Severity level (currently supports error, with info and warning planned for future releases)\n  - Alert message and supporting details\n  - Related entities (router, identity, service, etc.) associated with the alert\n\nExample alert event when a router cannot bind a configured network interface:\n\n```\n  {\n    \"namespace\": \"alert\",\n    \"event_src_id\": \"ctrl1\",\n    \"timestamp\": \"2021-11-08T14:45:45.785561479-05:00\",\n    \"alert_source_type\": \"router\",\n    \"alert_source_id\": \"DJFljCCoLs\",\n    \"severity\": \"error\",\n    \"message\": \"error starting proxy listener for service 'test'\",\n    \"details\": [\n      \"unable to bind eth0, no address\"\n    ],\n    \"related_entities\": {\n      \"router\": \"DJFljCCoLs\",\n      \"identity\": \"DJFljCCoLs\",\n      \"service\": \"3DPjxybDvXlo878CB0X2Zs\"\n    }\n  }\n```\n\nAlert events can be consumed through the standard event system and logged to configured event handlers for monitoring and alerting purposes.\n\nThese events are currently in Beta, as the format is still subject to change. Once they've been in use in production for a while\nand proven useful, they will be marked as stable.\n\n## Azure Service Bus Event Sink\n\n*Originally released in 1.7.0. Contributed by @ffaraone.*\n\nAdds support for streaming controller events to Azure Service Bus.\nThe new logger enables real-time event streaming from the OpenZiti controller to Azure Service Bus\nqueues or topics, providing integration with Azure-based monitoring and analytics systems.\n\nTo enable the Azure Service Bus event logger, add configuration to the controller config file under the events section:\n\n```\n  events:\n    serviceBusLogger:\n      subscriptions:\n        - type: circuit\n        - type: session\n        - type: metrics\n          sourceFilter: .*\n          metricFilter: .*\n        # Add other event types as needed\n      handler:\n        type: servicebus\n        format: json\n        connectionString: \"Endpoint=sb://your-namespace.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=your-key\"\n        topic: \"ziti-events\"          # Use 'topic' for Service Bus topic\n        # queue: \"ziti-events-queue\"  # Or use 'queue' for Service Bus queue\n        bufferSize: 100                # Optional, defaults to 50\n```\n\n- Required configuration:\n    - format: Event format, currently supports only json\n    - connectionString: Azure Service Bus connection string\n    - Either topic or queue: Destination name (mutually exclusive)\n\n- Optional configuration:\n    - bufferSize: Internal message buffer size (default: 50)\n\n## OIDC is now enabled by default\n\nThe controller now automatically adds the `edge-oidc` API binding to any web listener that hosts\nthe `edge-client` API, even when `edge-oidc` is not explicitly listed in the `web` section of the\nconfiguration. This means OIDC-based authentication is available out of the box without requiring\nchanges to existing controller configurations.\n\n### Where OIDC binds\n\nThe `edge-oidc` binding is added to the same web listener(s) as `edge-client`. If `edge-client` is\nhosted on `127.0.0.1:1280`, the OIDC endpoints will be available on that same address under the\n`/oidc` path (e.g. `https://127.0.0.1:1280/oidc/.well-known/openid-configuration`).\n\nThe controller capabilities returned by `GET /version` will include `OIDC_AUTH` and the\n`edge-oidc` entry will be present in `apiVersions` when OIDC is active.\n\n### Opting out\n\nIf OIDC should not be enabled automatically, set `disableOidcAutoBinding: true` under `edge.api`:\n\n```yaml\nedge:\n  api:\n    address: 127.0.0.1:1280\n    sessionTimeout: 30m\n    disableOidcAutoBinding: true\n```\n\nWhen `disableOidcAutoBinding` is `true`, OIDC will only be active if `edge-oidc` is explicitly\nlisted as a binding in the `web` section. \n\nWhen OIDC is not enabled, all (SDK) clients will revert to using legacy authentication.\nLegacy authentication does not support multiple controllers or HA in general. Clients will treat\ntheir controller as if it is a single controller instance and will function as if no other controllers\nexist.\n\n\n## WWW-Authenticate Headers\n\nThe controller's Edge APIs now include `WWW-Authenticate` headers on `401 Unauthorized` responses,\nper issuer: https://github.com/openziti/ziti/issues/3356. These headers provide insight into why\na token was rejected. The main benefit of these headers is to convey information for JWT backed\nAPI Sessions and API Session authentication where a token may not have been provided (missing),\nis used beyond its expiration date (expired), or the token has become invalid for any other\nreason (invalid).\n\n`www-authenticate` headers are provided as a single header instance with multiple challenge values\nseparate by commas.\n\n### No Credentials Provided\n\nWhen a request hits a protected endpoint without any credentials, a single `WWW-Authenticate` header\nis returned listing both accepted auth schemes as comma-separated challenges:\n\n```\nWWW-Authenticate: zt-session realm=\"zt-session\" error=\"missing\" error_description=\"no matching token was provided\",Bearer realm=\"openziti-oidc\" error=\"missing\" error_description=\"no matching token was provided\"\n```\n\n### Token Errors\n\nWhen a token is present but cannot be accepted, the header identifies the scheme and what went wrong:\n\n```\nWWW-Authenticate: Bearer realm=\"openziti-oidc\" error=\"expired\" error_description=\"token expired\"\nWWW-Authenticate: Bearer realm=\"openziti-oidc\" error=\"invalid\" error_description=\"token is invalid\"\nWWW-Authenticate: zt-session realm=\"zt-session\" error=\"invalid\" error_description=\"token is invalid\"\n```\n\n### OIDC External JWT — Primary Authentication\n\nWhen an auth policy requires an external JWT signer for primary authentication (e.g., a PKCE flow\nbacked by an ext-jwt signer), the header identifies which signers are accepted and what went wrong.\nMultiple accepted signers are pipe-delimited in the `id` and `issuer` parameters:\n\n```\nWWW-Authenticate: Bearer realm=\"openziti-primary-ext-jwt\" error=\"missing\" error_description=\"no matching token was provided\" id=\"signer-id-1|signer-id-2\" issuer=\"https://issuer1.example.com|https://issuer2.example.com\"\n```\n\nThe `error` value follows the same `missing`/`expired`/`invalid` pattern as standard bearer token errors.\n\n### OIDC External JWT — Secondary / MFA Authentication\n\nWhen an auth policy requires an external JWT signer as a secondary factor (step-up after primary\nauth succeeds), the header identifies the single required signer. The `error` value follows the\nsame `missing`/`expired`/`invalid` pattern:\n\n```\nWWW-Authenticate: Bearer realm=\"openziti-secondary-ext-jwt\" error=\"missing\" error_description=\"no matching token was provided\" id=\"<signer-id>\" issuer=\"<issuer>\"\n```\n\n### Anonymous Endpoints\n\nUnauthenticated endpoints such as version information do not return `WWW-Authenticate` headers.\n\n## HA Preferred Leaders\n\nControllers can be marked as a preferred leader. \n\n**Example Config**\n```yaml\ncluster:\n  dataDir: /home/{{ .Model.MustVariable \"credentials.ssh.username\" }}/fablab/ctrldata\n  preferredLeader: true\n```\n\nIf a controller that is not marked preferredLeader becomes a preferredLeader, it will check \nif there's a node available that is marked as preferred. If there is one, or one later\njoins the cluster, the non-preferred node will attempt to transfer leadership to the \nnode that is marked as preferred.\n\n## Expanded Dynamic Cost Range\n\nThe dynamic cost range for smart routing has been expanded beyond the previous 64K limit. Under high\nload, terminators could saturate the cost space, making dynamic cost values meaningless for routing\ndecisions and leading to uneven load distribution. The expanded range allows for more granular cost\ndifferentiation even under heavy load.\n\n## Circuit ID and Error in Dial Failures\n\nDial failures now return the circuit ID and, when available, the error that caused the circuit to fail.\nPreviously, the circuit ID was only returned on successful dials. Note that SDKs will need to be\nupdated to surface the circuit id when a dial failure happens.\n\n## Multi-Underlay Control Channels\n\nRouter-to-controller control channels now support multiple underlays with priority-based message routing.\nThis allows time-sensitive control messages (heartbeats, routing, circuit requests) to be separated from\noperational data (metrics, inspections) across dedicated TCP connections, preventing bulk operations from\ndelaying user-affecting control plane traffic. This feature does not yet allow specifying multiple \nnetwork interfaces to use, to load balance data across.\n\n## Dialing Identity Forwarded to Hosting SDK\n\nThe identity ID and name of the dialing client are now forwarded to the hosting SDK when a circuit is\nestablished. This allows hosting applications to identify which identity initiated the connection,\nenabling identity-aware request handling on the server side. This will require SDK updates to add this\nto the API for hosting applications.\n\n## Controller-Initiated Control Channel Dials (BETA)\n\nControllers can now dial routers to establish control channels. Previously, routers were solely\nresponsible for dialing controllers. This feature is designed for deployments where one or more\ncontrollers are in a private network that routers cannot reach, but the controllers can dial out.\nA common scenario is an HA cluster where some controllers are publicly reachable and some are in\na private network. External routers connect to the public controllers normally, and the private\ncontrollers dial out to the routers.\n\n### Router Configuration\n\nRouters can configure one or more control channel listeners. Each listener specifies a bind address,\nan advertise address (reported to the controller), and optional groups for matching.\n\n```yaml\nctrl:\n  listeners:\n    - bind: tls://0.0.0.0:6262\n      advertise: tls://router.example.com:6262\n      groups:\n        - default\n```\n\nThe router is the authoritative source of ctrl channel listener information, similar to link\nlisteners. When a router connects to any controller, it reports its configured `ctrlChanListeners`\nand the controller data model is updated automatically. This means that in most deployments —\nwhere the router can reach at least one controller — no manual configuration of listener addresses\nis needed on the controller side.\n\nThe `ctrlChanListeners` field can also be set via the CLI for cases where a router cannot reach\nany controller and thus cannot initialize the data model itself:\n\n```bash\nziti edge update edge-router myRouter --bootstrap-ctrl-chan-listener 'tls://router.example.com:6262=group1,group2'\n```\n\nGroups default to `[\"default\"]` if not specified.\n\n### Controller Configuration\n\nThe controller dialer is disabled by default and must be explicitly enabled. When enabled, the\ncontroller will dial routers that have control channel listeners configured and are not already\nconnected.\n\n```yaml\nctrl:\n  dialer:\n    enabled: true\n    groups:\n      - default\n    dialDelay: 30s\n    minRetryInterval: 1s\n    maxRetryInterval: 5m\n    retryBackoffFactor: 1.5\n    fastFailureWindow: 5s\n    queueSize: 32\n    maxWorkers: 10\n```\n\n- `enabled` - Enables the controller dialer (default: `false`)\n- `groups` - List of groups to match against router listener groups (default: `[\"default\"]`)\n- `dialDelay` - Delay before the controller starts dialing after boot (default: `30s`)\n- `minRetryInterval` - Minimum backoff delay between dial retries (default: `1s`)\n- `maxRetryInterval` - Maximum backoff delay between dial retries (default: `5m`)\n- `retryBackoffFactor` - Multiplier applied to the retry delay on each failure, jittered +/- 0.5 (default: `1.5`)\n- `fastFailureWindow` - If a connection drops within this window after connecting, backoff continues rather than resetting (default: `5s`)\n- `queueSize` - Maximum number of pending dial jobs in the worker pool queue (default: `32`)\n- `maxWorkers` - Maximum number of concurrent dial workers (default: `10`)\n\nThe controller will only dial routers whose listener groups overlap with the controller's configured\ngroups. When a router advertises multiple ctrl channel listener addresses, the dialer rotates through\nthem on each failure so that an unreachable address does not block attempts to the others.\n\n### Metrics\n\nThe controller dialer worker pool exposes the following metrics under the `ctrl_channel.dialer` prefix:\n\n- `ctrl_channel.dialer.queue_size` - Current number of pending dial jobs in the queue\n- `ctrl_channel.dialer.worker_count` - Current number of dial worker goroutines\n- `ctrl_channel.dialer.busy_workers` - Number of workers currently executing a dial\n- `ctrl_channel.dialer.work_timer` - Timer tracking the duration of each dial attempt\n\n## SDK Inspection Support\n\nNew CLI commands have been added for inspecting SDK state, useful for diagnosing terminator and\nconnectivity issues.\n\n**Note:** SDK inspection requires SDK support. Currently only the Go SDK supports inspection,\nas of version 1.5.0. Other SDKs will need to add support before these commands can be used\nwith them.\n\n### `ziti fabric inspect sdk`\n\nRetrieves SDK context inspection data from identities connected to routers.\n\n```\nziti fabric inspect sdk <target-selector> <identity-id>\n```\n\nThe `<target-selector>` is a regex matching router IDs (use `.*` for all routers). The\n`<identity-id>` is the identity whose SDK context you want to inspect. The command returns\ndetailed state from the connected SDK instance, including active services, terminators, and\nconnection status.\n\n### `ziti agent tunnel dump-sdk`\n\nDumps SDK context information from a running `ziti tunnel` process via the IPC agent.\n\n```\nziti agent tunnel dump-sdk\n```\n\nReturns JSON-formatted inspection data for all SDK contexts registered in the tunnel process,\nincluding service listeners, connections, and terminator state.\n\n## Revocation System Improvements\n\nWhen a session is refreshed, the old refresh token's revocation is no longer created\nsynchronously through raft. Instead, revocations are queued in memory and flushed in\nbatches on a configurable interval. This removes the database and raft as a bottleneck\non token refreshes. If the old token is close to expiring, the revocation is skipped\nentirely.\n\nNew configuration tunables under `edge.oidc`:\n\n| Key | Default | Description |\n|-----|---------|-------------|\n| `revocationMinTokenLifetime` | unset | Skip revocation if the old token expires within this duration (must be < 50% of `refreshTokenDuration`) |\n| `revocationBucketInterval` | `1m` | Bucket window for batching revocations before flushing through raft |\n| `revocationBucketMaxSize` | `200` | Max revocations per raft entry |\n| `revocationMaxQueued` | `25000` | Max revocations queued in memory before dropping |\n| `revocationEnforcerFrequency` | `1m` | How often expired revocations are purged (leader only) |\n\n## CLI Reorganization Details\n\nThe CLI has been reorganized so that edge and fabric entity management commands are \navailable directly at the top level. The `ziti edge` and `ziti fabric` command trees \nremain fully functional — the new top-level commands are additional entry points, not \nreplacements.\n\n### Top-Level CRUD Commands\n\nEntity create, delete, list, and update operations that previously required the \n`ziti edge` or `ziti fabric` prefix are now available directly:\n\n| New command | Previous command |\n|---|---|\n| `ziti create identity ...` | `ziti edge create identity ...` |\n| `ziti delete service ...` | `ziti edge delete service ...` |\n| `ziti list edge-routers` | `ziti edge list edge-routers` |\n| `ziti update identity ...` | `ziti edge update identity ...` |\n| `ziti list circuits` | `ziti fabric list circuits` |\n| `ziti delete terminator ...` | `ziti fabric delete terminator ...` |\n\nAll edge and fabric entities are available under the consolidated commands. When an \nentity name exists in both edge and fabric (e.g., `service`, `router`), the edge \nversion is the default. Fabric equivalents are accessible with a `fabric-` prefix \n(e.g., `ziti list fabric-services`).\n\n### Top-Level Login\n\nSession management is now available at the top level:\n\n| New command | Previous command |\n|---|---|\n| `ziti login` | `ziti edge login` |\n| `ziti login forget` | `ziti edge login forget` |\n| `ziti login use` | `ziti edge use` |\n\n### Breaking Change: `ziti create ca`\n\n`ziti create ca` now creates a Ziti edge Certificate Authority, matching the \nbehavior of `ziti edge create ca`. Previously, `ziti create ca` was a PKI command \nfor generating CA certificates on the local filesystem.\n\nThe PKI command is still available at its original location:\n\n```\nziti pki create ca ...\n```\n\nScripts that use `ziti create ca` for PKI operations should be updated to use \n`ziti pki create ca` instead.\n\n## Multiple DNS Upstreams\n\nThe tunneler's DNS resolver now accepts multiple upstream DNS servers and fans out recursive queries\nto all of them in parallel, rather than being limited to a single upstream. This is useful for split-horizon\nsetups where different resolvers are authoritative for different zones, and for environments where a primary\nresolver may be slow or unreachable.\n\n### CLI\n\nThe `ziti tunnel --dnsUpstream` flag is now a repeatable string slice. Upstreams can be listed by repeating\nthe flag or by passing a comma-separated value:\n\n```bash\nziti tunnel run \\\n  --dnsUpstream udp://10.96.0.10:53 \\\n  --dnsUpstream tcp://8.8.8.8:53\n```\n\n### Router Config\n\nIn `xgress_edge_tunnel` router configs, `options.dnsUpstream` now accepts either a single string (as before)\nor a list of strings. Existing configs continue to work unchanged:\n\n```yaml\n# single upstream (unchanged)\noptions:\n  dnsUpstream: udp://10.96.0.10:53\n\n# multiple upstreams\noptions:\n  dnsUpstream:\n    - udp://10.96.0.10:53\n    - tcp://8.8.8.8:53\n```\n\n### How Resolution Works\n\nWhen a query comes in, the resolver dispatches it to every configured upstream concurrently. The first\nresponse with `RCODE=NOERROR` wins and is returned to the client immediately, so split-horizon lookups\nwork even if one upstream is slow. If no upstream returns NOERROR, the resolver picks the best-ranked\nnon-NOERROR reply (NXDOMAIN > SERVFAIL > REFUSED) so authoritative negative answers aren't masked by\ntransport failures. If every upstream fails to respond, the query is treated as unanswerable and handled\nper the configured `dnsUnanswerable` disposition.\n\n## Current Beta Features\n\nBeta features are still under development and are subject to change. They should\nbe usable in their released form. Though unlikely, there is a small chance they will \nbe removed. \n\n* Basic Permission System\n* Alert Events\n* Controller-Initiated Control Channel Dials\n\n## Component Updates and Bug Fixes\n\n* github.com/openziti/agent: [v1.0.31 -> v1.0.33](https://github.com/openziti/agent/compare/v1.0.31...v1.0.33)\n* github.com/openziti/channel/v4: [v4.2.28 -> v4.3.11](https://github.com/openziti/channel/compare/v4.2.28...v4.3.11)\n    * [Issue #242](https://github.com/openziti/channel/issues/242) - Reconnecting channel shouldn't allow changing ids\n    * [Issue #235](https://github.com/openziti/channel/issues/235) - Bump allowed hello message headers size to 16k from 4k\n    * [Issue #228](https://github.com/openziti/channel/issues/228) - Ensure that Underlay never return nil on MultiChannel\n    * [Issue #226](https://github.com/openziti/channel/issues/226) - Allow specifying a minimum number of underlays for a channel, regardless of underlay type\n    * [Issue #225](https://github.com/openziti/channel/issues/225) - Add ChannelCreated to the UnderlayHandler API to allow handlers to be initialized with the channel before binding\n    * [Issue #224](https://github.com/openziti/channel/issues/224) - Update the underlay dispatcher to allow unknown underlay types to fall through to the default\n    * [Issue #222](https://github.com/openziti/channel/issues/222) - Allow injecting the underlay type into messages\n\n* github.com/openziti/edge-api: [v0.26.47 -> v0.28.1](https://github.com/openziti/edge-api/compare/v0.26.47...v0.28.1)\n    * [Issue #175](https://github.com/openziti/edge-api/issues/175) - ctrlChanListeners should have x-omit-empty: false attribute\n    * [Issue #170](https://github.com/openziti/edge-api/issues/170) - Add preferredLeader flag to controllers\n    * [Issue #167](https://github.com/openziti/edge-api/issues/167) - Add ctrlChanListeners to router types\n    * [Issue #164](https://github.com/openziti/edge-api/issues/164) - Add permissions list to identity\n\n* github.com/openziti/foundation/v2: [v2.0.72 -> v2.0.90](https://github.com/openziti/foundation/compare/v2.0.72...v2.0.90)\n    * [Issue #472](https://github.com/openziti/foundation/issues/472) - Add support for multi-bit set/get to AtomicBitSet\n    * [Issue #464](https://github.com/openziti/foundation/issues/464) - Add support for -pre in versions\n    * [Issue #455](https://github.com/openziti/foundation/issues/455) - Correctly close goroutine pool when external close is signaled\n    * [Issue #452](https://github.com/openziti/foundation/issues/452) - Goroutine pool with a min worker count of 1 can drop to 0 workers due to race condition\n\n* github.com/openziti/identity: [v1.0.111 -> v1.0.128](https://github.com/openziti/identity/compare/v1.0.111...v1.0.128)\n    * [Issue #68](https://github.com/openziti/identity/issues/68) - Shutdown file watcher when stopping identity watcher\n\n* github.com/openziti/metrics: [v1.4.2 -> v1.4.5](https://github.com/openziti/metrics/compare/v1.4.2...v1.4.5)\n    * [Issue #58](https://github.com/openziti/metrics/issues/58) - Add GaugeFloat64 support\n    * [Issue #56](https://github.com/openziti/metrics/issues/56) - underlying resources of reference counted meters are not cleaned up when reference count hits zero\n\n* github.com/openziti/runzmd: [v1.0.80 -> v1.0.90](https://github.com/openziti/runzmd/compare/v1.0.80...v1.0.90)\n* github.com/openziti/sdk-golang: [v1.2.3 -> v1.7.0](https://github.com/openziti/sdk-golang/compare/v1.2.3...v1.7.0)\n    * [Issue #901](https://github.com/openziti/sdk-golang/issues/901) - Move xgress back to having retransmitter goroutine per-xgress\n    * [Issue #906](https://github.com/openziti/sdk-golang/issues/906) - Fix potential nil references on session service structs\n    * [Issue #897](https://github.com/openziti/sdk-golang/issues/897) - Allow xgress to use pull model for reads when appropriate\n    * [Issue #895](https://github.com/openziti/sdk-golang/issues/895) - Limit effect sudden rtt spikes can have on rtt moving average\n    * [Issue #902](https://github.com/openziti/sdk-golang/issues/902) - Inspect response message content types are mixed up\n    * [Issue #887](https://github.com/openziti/sdk-golang/issues/887) - Fix listener manager cleanup\n    * [Issue #886](https://github.com/openziti/sdk-golang/issues/886) - When controller is busy during service refresh, backoff and retry instead of falling back to full refresh\n    * [Issue #885](https://github.com/openziti/sdk-golang/issues/885) - Only compare relevant service fields when looking for changes\n    * [Issue #884](https://github.com/openziti/sdk-golang/issues/884) - Add deadline for bind establishment\n    * [Issue #883](https://github.com/openziti/sdk-golang/issues/883) - Router level listener can be left open if multi-listener closes during listener establishment\n    * [Issue #877](https://github.com/openziti/sdk-golang/issues/877) - Handle differences in xgress eof/end-of-circuit handling by adding a capabilities exchange\n    * [Issue #832](https://github.com/openziti/sdk-golang/issues/832) - Fuzz session refresh timers\n    * [Issue #879](https://github.com/openziti/sdk-golang/issues/879) - Return the connId in inspect response\n    * [Issue #878](https://github.com/openziti/sdk-golang/issues/878) - Fix responses from rx goroutines\n    * [Issue #874](https://github.com/openziti/sdk-golang/issues/874) - Add inspect support at the context level\n    * [Issue #871](https://github.com/openziti/sdk-golang/issues/871) - Make SDK better at sticking to MaxTerminator terminators\n    * [Issue #708](https://github.com/openziti/sdk-golang/issues/708) - Support for Go's built-in context in Dial methods\n    * [Issue #860](https://github.com/openziti/sdk-golang/issues/860) - Make the dialing identity's id and name available on dialed connections\n    * [Issue #857](https://github.com/openziti/sdk-golang/issues/857) - Use new error code and retry hints to correctly react to terminator errors\n    * [Issue #847](https://github.com/openziti/sdk-golang/issues/847) - Ensure the initial version check succeeds, to ensure we don't legacy sessions on ha or oidc-enabled controllers\n    * [Issue #824](https://github.com/openziti/sdk-golang/pull/824) - release notes and hard errors on no TOTP handler breaks partial auth events\n    * [Issue #818](https://github.com/openziti/sdk-golang/issues/818) - Full re-auth should not clear services list, as that breaks the on-change logic\n    * [Issue #817](https://github.com/openziti/sdk-golang/issues/817) - goroutines can get stuck when iterating over randomized HA controller list\n    * [Issue #736](https://github.com/openziti/sdk-golang/issues/736) - Migrate from github.com/mailru/easyjson\n    * [Issue #813](https://github.com/openziti/sdk-golang/issues/813) - SDK doesn't stop close listener when it detects that a service being hosted gets deleted\n    * [Issue #811](https://github.com/openziti/sdk-golang/issues/811) - Credentials are lost when explicitly set\n    * [Issue #807](https://github.com/openziti/sdk-golang/issues/807) - Don't send close from rxer to avoid blocking\n    * [Issue #800](https://github.com/openziti/sdk-golang/issues/800) - Tidy create service session logging\n\n* github.com/openziti/secretstream: [v0.1.39 -> v0.1.49](https://github.com/openziti/secretstream/compare/v0.1.39...v0.1.49)\n* github.com/openziti/storage: [v0.4.26 -> v0.4.39](https://github.com/openziti/storage/compare/v0.4.26...v0.4.39)\n    * [Issue #122](https://github.com/openziti/storage/issues/122) - StringFuncNode has incorrect nil check, allowing panic\n    * [Issue #120](https://github.com/openziti/storage/issues/120) - Change post tx commit constraint handling order\n    * [Issue #119](https://github.com/openziti/storage/issues/119) - Add ContextDecorator API\n\n* github.com/openziti/transport/v2: [v2.0.188 -> v2.0.215](https://github.com/openziti/transport/compare/v2.0.188...v2.0.215)\n    * [Issue #31](https://github.com/openziti/transport/issues/31) - ipv6 Transport Address Parsing\n    * [Issue #149](https://github.com/openziti/transport/issues/149) - Archive transwarp code\n\n* github.com/openziti/xweb/v3: [v2.3.4 -> v3.0.4](https://github.com/openziti/xweb/compare/v2.3.4...v3.0.4)\n    * [Issue #32](https://github.com/openziti/xweb/issues/32) - watched identities sometimes don't reload when changed\n\n* github.com/openziti/go-term-markdown: v1.0.1 (new)\n* github.com/openziti/ziti/v2: [v1.6.8 -> v2.0.0](https://github.com/openziti/ziti/compare/v1.6.8...v2.0.0)\n    * [Issue #3816](https://github.com/openziti/ziti/issues/3816) - Support multiple upstream DNS providers in ziti tunnel and ER/T\n    * [Issue #3699](https://github.com/openziti/ziti/issues/3699) - Consolidate CLI edge and fabric commands in top level create/update/delete/list/login commands\n    * [Issue #3788](https://github.com/openziti/ziti/issues/3788) - OIDC Endpoints return 400 Bad Request instead of underlying error\n    * [Issue #3680](https://github.com/openziti/ziti/issues/3680) - adds revocation control to CLI and Management API\n    * [Issue #3717](https://github.com/openziti/ziti/issues/3717) - Generic error message for specific error\n    * [Issue #3543](https://github.com/openziti/ziti/issues/3543) - New Circuit Failure code for sockets not available\n    * [Issue #3364](https://github.com/openziti/ziti/issues/3364) - Make no such host error specific\n    * [Issue #2888](https://github.com/openziti/ziti/issues/2888) - New specific Error code for port not allowed\n    * [Issue #2859](https://github.com/openziti/ziti/issues/2859) - Create specific error code for DNS failed resolution\n    * [Issue #1580](https://github.com/openziti/ziti/issues/1580) - Invalid link destination should have a specific error code\n    * [Issue #3706](https://github.com/openziti/ziti/issues/3706) - Increase link payload/ack queue sizes and make them configurable\n    * [Issue #3778](https://github.com/openziti/ziti/issues/3778) - SetRouterDataModel can deadlock in the router\n    * [Issue #3777](https://github.com/openziti/ziti/issues/3777) - With the new circuit reserve, we can have circuits with no path in the controller circuit set, which can cause panics\n    * [Issue #3770](https://github.com/openziti/ziti/issues/3770) - Update Token Requests Should Close Channel Connections If Invalid\n    * [Issue #3762](https://github.com/openziti/ziti/issues/3762) - Revocations not included in full router data model state\n    * [Issue #3757](https://github.com/openziti/ziti/issues/3757) - Mesh peer signing cert from header is overwritten by TLS underlay cert\n    * [Issue #3756](https://github.com/openziti/ziti/issues/3756) - TLS handshake rate limiter timeout check reads from wrong config scope\n    * [Issue #3755](https://github.com/openziti/ziti/issues/3755) - commandHandler config read from wrong scope\n    * [Issue #3754](https://github.com/openziti/ziti/issues/3754) - dialFailed drops applyFailed parameter, preventing duplicate link retry jitter\n    * [Issue #3753](https://github.com/openziti/ziti/issues/3753) - SPIFFE trust domain prefix check has swapped HasPrefix arguments\n    * [Issue #3746](https://github.com/openziti/ziti/issues/3746) - The controller connect events control channel handler leaks a goroutine\n    * [Issue #3747](https://github.com/openziti/ziti/issues/3747) - Update controller peer error marshalling for app code changes\n    * [Issue #3721](https://github.com/openziti/ziti/issues/3721) - Add CreateCircuitV3 to controller\n    * [Issue #3719](https://github.com/openziti/ziti/issues/3719) - Allow binding specific inspects to pass through to xgress listener implementations\n    * [Issue #3696](https://github.com/openziti/ziti/issues/3696) - oidc provider is non-deterministic for wildcard certs\n    * [Issue #3681](https://github.com/openziti/ziti/issues/3681) - coalesce OIDC JWT revocations to reduce controller write pressure\n    * [Issue #3683](https://github.com/openziti/ziti/issues/3683) - add fablab test for testing flow control changes over a longer term\n    * [Issue #3673](https://github.com/openziti/ziti/issues/3673) - revocation build-up in db and rdm\n    * [Issue #3674](https://github.com/openziti/ziti/issues/3674) - Update to Go 1.26\n    * [Issue #3496](https://github.com/openziti/ziti/issues/3496) - MFA TOTP Enrollment During OIDC Authentication Does Not Work\n    * [Issue #3609](https://github.com/openziti/ziti/issues/3609) - Stabilize terminator creation test for 2.0\n    * [Issue #3648](https://github.com/openziti/ziti/issues/3648) - tunnel: myCopy logs router ID as circuitId, causing misleading debug output\n    * [Issue #3658](https://github.com/openziti/ziti/issues/3658) - Raft cluster join fails with \"hello message too big\" when using long hostnames\n    * [Issue #3626](https://github.com/openziti/ziti/issues/3626) - controller: overlay bind point produces malformed URL in /versions apiBaseUrls\n    * [Issue #3635](https://github.com/openziti/ziti/issues/3635) - Allow controllers to dial routers to support more topologies\n    * [Issue #3607](https://github.com/openziti/ziti/issues/3607) - linux installer not upgradable from v1\n    * [Issue #3650](https://github.com/openziti/ziti/issues/3650) - Reroute doesn't proactively clean up orphaned route entries\n    * [Issue #3571](https://github.com/openziti/ziti/issues/3571) - Ensure 2.0 backwards compatibility with 1.6 and 1.5 using the smoketest\n    * [Issue #3636](https://github.com/openziti/ziti/issues/3636) - Adaptive rate limiter should use success rate rather than queue position\n    * [Issue #3600](https://github.com/openziti/ziti/issues/3600) - Add a preferredLeader flag, allow selected nodes to be preferred for raft leader, if they're available\n    * [Issue #3642](https://github.com/openziti/ziti/issues/3642) - Use xgress_common.Connection type for xgress_transport and xgress_proxy\n    * [Issue #3597](https://github.com/openziti/ziti/issues/3597) - Enable OIDC API by default\n    * [Issue #3624](https://github.com/openziti/ziti/issues/3624) - Multi-underlay control channel doesn't correctly handle lack of group secret on non-grouped underlays\n    * [Issue #3613](https://github.com/openziti/ziti/issues/3613) - Initializing cluster from existing db using `db:` config settings results in panic\n    * [Issue #3620](https://github.com/openziti/ziti/issues/3620) - Controller control channel heartbeats config parsing was erroneously nested under options parsing\n    * [Issue #3619](https://github.com/openziti/ziti/issues/3619) - Router connect events full sync interval was using min/max values meant for the batch interval\n    * [Issue #3618](https://github.com/openziti/ziti/issues/3618) - Router interfaceDiscovery.minReportInterval value was being set to checkInterval\n    * [Issue #3617](https://github.com/openziti/ziti/issues/3617) - Transit router disabled flag not passed through raft command structure\n    * [Issue #3333](https://github.com/openziti/ziti/issues/3333) - Updb user-lockout triggered by successful login attempts\n    * [Issue #3599](https://github.com/openziti/ziti/issues/3599) - Add gap detection and handling to router data model\n    * [Issue #3356](https://github.com/openziti/ziti/issues/3356) - Add WWW-Authenticate Headers\n    * [Issue #3074](https://github.com/openziti/ziti/issues/3074) - Dynamic cost range is too limited\n    * [Issue #3558](https://github.com/openziti/ziti/issues/3558) - terminator cost increased on egress dial success, not on circuit completion\n    * [Issue #3556](https://github.com/openziti/ziti/issues/3556) - global circuit costs not cleared when terminator is deleted\n    * [Issue #3557](https://github.com/openziti/ziti/issues/3557) - costing calculation for the weighted terminator selection strategy  is incorrect\n    * [Issue #2512](https://github.com/openziti/ziti/issues/2512) - Return circuit ID and error in dial failures\n    * [Issue #3569](https://github.com/openziti/ziti/issues/3569) - Version 2.0+ routers should not connect to controllers which do not support JWT formatted legacy sessions\n    * [Issue #3565](https://github.com/openziti/ziti/issues/3565) - Link dialer save 'is first conn' true, so all dials claim to be first, causing potential race condition\n    * [Issue #3575](https://github.com/openziti/ziti/issues/3575) - OIDC token endpoint code bugs possibly resulting in panics/eof errors\n    * [Issue #3550](https://github.com/openziti/ziti/issues/3550) - Support multi-underlay control channels\n    * [Issue #3535](https://github.com/openziti/ziti/issues/3535) - Remove the legacy xgress_edge_tunnel implementation\n    * [Issue #3547](https://github.com/openziti/ziti/issues/3547) - Add support for sending the dialing identity id and name to the hosting sdk\n    * [Issue #3541](https://github.com/openziti/ziti/issues/3541) - Remove option to disable the router data model in the controller\n    * [Issue #3540](https://github.com/openziti/ziti/issues/3540) - Handle UDP difference between proxy and tproxy implementations\n    * [Issue #3527](https://github.com/openziti/ziti/issues/3527) - ER/T UDP tunnels keep closed connections for 30s, preventing potential new good connections in that time\n    * [Issue #3526](https://github.com/openziti/ziti/issues/3526) - ER/T half-close logic is incorrect\n    * [Issue #3524](https://github.com/openziti/ziti/issues/3524) - Provide more error context to SDKs for terminator errors\n    * [Issue #3509](https://github.com/openziti/ziti/issues/3509) - Enforce policy on the router for oidc sessions, by closing open circuits and terminators when service access is lost\n    * [Issue #3531](https://github.com/openziti/ziti/issues/3531) - Remove created/updated/deleted terminator events. Obsoleted by entity change events.\n    * [Issue #3532](https://github.com/openziti/ziti/issues/3532) - Removed deprecated create identity <type> subcommands\n    * [Issue #3521](https://github.com/openziti/ziti/issues/3521) - Cleanup CLI to remove calls to os.Exit to be embed friendlier\n    * [Issue #3516](https://github.com/openziti/ziti/issues/3516) - Remove support for create terminator v1\n    * [Issue #3512](https://github.com/openziti/ziti/issues/3512) - Remove legacy link management code from the controller\n    * [Issue #3511](https://github.com/openziti/ziti/issues/3511) - router proxy mode fails to resolve interface if binding is 0.0.0.0\n    * [Issue #3503](https://github.com/openziti/ziti/issues/3503) - Allow routers to request current cluster membership information\n    * [Issue #3501](https://github.com/openziti/ziti/issues/3501) - Get cluster membership information from raft directly, rather than trying to cache it in the DB\n    * [Issue #3500](https://github.com/openziti/ziti/issues/3500) - Set a router data model timeline when initializing a new HA setup, rather than letting it stay blank\n    * [Issue #3504](https://github.com/openziti/ziti/issues/3504) - Reduce router data model full state updates\n    * [Issue #3492](https://github.com/openziti/ziti/pull/3492) - Bump openziti/ziti-console-assets from 3.12.9 to 4.0.0 in /dist/docker-images/ziti-controller in the all group\n    * [Issue #3484](https://github.com/openziti/ziti/issues/3484) - router ctrl channel handler for handling cluster changes has an initialization race condition\n    * [Issue #3477](https://github.com/openziti/ziti/issues/3477) - Optionally enable model changes triggered by login to be non-blocking and to be droppable if the system is under load\n    * [Issue #3473](https://github.com/openziti/ziti/issues/3473) - Enable tls handshake rate limiter by default and tweak default values.\n    * [Issue #3471](https://github.com/openziti/ziti/issues/3471) - Go tunneler is ignoring host config MaxConnections\n    * [Issue #3469](https://github.com/openziti/ziti/issues/3469) - Only send model updates on resubscribe if the RDM index has advanced\n    * [Issue #2573](https://github.com/openziti/ziti/issues/2573) - An edge router in a tight restart loop causes a resource leak on routers to which it connects.\n    * [Issue #3430](https://github.com/openziti/ziti/issues/3430) - Add permissions list to identity\n    * [Issue #2109](https://github.com/openziti/ziti/issues/2109) - Add Edge Management Read Only Capability\n    * [Issue #3435](https://github.com/openziti/ziti/issues/3435) - Add edge management API permissions by entity type and action\n    * [Issue #3441](https://github.com/openziti/ziti/issues/3441) - Update router connection tracker to interrogate active connections\n    * [Issue #3451](https://github.com/openziti/ziti/issues/3451) - ci - compare only stable releases when promoting\n    * [Issue #3437](https://github.com/openziti/ziti/issues/3437) - SDK OIDC token updates to routers should return an error if invalid\n    * [Issue #3348](https://github.com/openziti/ziti/issues/3348) - Unable to clear/reset the \"tags\" property on an entity to an empty object\n    * [Issue #3452](https://github.com/openziti/ziti/issues/3452) - `ziti agent cluster add` has bad behavior if the add address doesn't match the advertise address\n    * [Issue #3410](https://github.com/openziti/ziti/issues/3410) - Consolidate fabric REST API code with edge management and edge client code\n    * [Issue #3425](https://github.com/openziti/ziti/issues/3425) - RDM not properly responding to tunneler enabled flag changes\n    * [Issue #3420](https://github.com/openziti/ziti/issues/3420) - The terminator id cache uses the same id for all terminators in a host.v2 config, resulting in a single terminator\n    * [Issue #3419](https://github.com/openziti/ziti/issues/3419) - When using the router data model, precedence specified on the per-service identity mapping are incorrectly interpreted\n    * [Issue #3318](https://github.com/openziti/ziti/issues/3318) - Terminator creation seems to slow exponentially as the number of terminators rises from 10k to 20k to 40k\n    * [Issue #3407](https://github.com/openziti/ziti/issues/3407) - The CLI doesn't properly pass JWT authentication information to websocket endpoints\n    * [Issue #3359](https://github.com/openziti/ziti/issues/3359) - Ensure router data model subscriptions have reasonable performance and will scale\n    * [Issue #3354](https://github.com/openziti/ziti/issues/3354) - SDK/ENV Info from SDKs is not distributed in HA\n    * [Issue #3381](https://github.com/openziti/ziti/issues/3381) - the fabric service REST apis are missing the maxIdleTime property\n    * [Issue #3382](https://github.com/openziti/ziti/issues/3382) - Legacy service sessions generated pre-1.7.x are incompatible with v1.7.+ and need to be cleared\n    * [Issue #3339](https://github.com/openziti/ziti/issues/3339) - get router ctrl.endpoint from ctrls claim in JWT\n    * [Issue #3378](https://github.com/openziti/ziti/issues/3378) - login with file stopped working\n    * [Issue #3349](https://github.com/openziti/ziti/issues/3349) - UPDB OIDC login returns wrong content type\n    * [Issue #2324](https://github.com/openziti/ziti/issues/2324) - Add Ext-JWT/OIDC enrollment\n    * [Issue #3346](https://github.com/openziti/ziti/issues/3346) - Fix confusing attempt logging\n    * [Issue #3337](https://github.com/openziti/ziti/issues/3337) - Router reports \"no xgress edge forwarder for circuit\"\n    * [Issue #3345](https://github.com/openziti/ziti/issues/3345) - Clean up connect events tests and remove global XG registry\n    * [Issue #3264](https://github.com/openziti/ziti/issues/3264) - Allow routers to generate alert events in cases of service misconfiguration\n    * [Issue #3321](https://github.com/openziti/ziti/issues/3321) - Health Check API missing base path on discovery endpoint\n    * [Issue #3323](https://github.com/openziti/ziti/issues/3323) - router/tunnel static services fail to bind unless new param protocol is defined\n    * [Issue #3309](https://github.com/openziti/ziti/issues/3309) - Detect link connections meant for another router\n    * [Issue #3286](https://github.com/openziti/ziti/issues/3286) - edge-api binding doesn't have the correct path on discovery endpoints\n    * [Issue #3297](https://github.com/openziti/ziti/issues/3297) - stop promoting hotfixes downstream\n    * [Issue #3295](https://github.com/openziti/ziti/issues/3295) - make ziti tunnel service:port pairs optional\n    * [Issue #3291](https://github.com/openziti/ziti/issues/3291) - replace decommissioned bitnami/kubectl\n    * [Issue #3277](https://github.com/openziti/ziti/issues/3277) - Router can deadlock on closing a connection if the incoming data channel is full\n    * [Issue #3269](https://github.com/openziti/ziti/issues/3269) - Add host-interfaces config type\n    * [Issue #3258](https://github.com/openziti/ziti/issues/3258) - Add config type proxy.v1 so proxies can be defined dynamically for the ER/T\n    * [Issue #3259](https://github.com/openziti/ziti/issues/3259) - Interfaces config type not added due to wrong name\n    * [Issue #3265](https://github.com/openziti/ziti/issues/3265) - Forwarding errors should log at debug, since they are usual part of circuit teardown\n    * [Issue #3261](https://github.com/openziti/ziti/issues/3261) - ER/T dialed xgress connections may only half-close when peer is fully closed\n\n\n","reactions":{"url":"https://api.github.com/repos/openziti/ziti/releases/311377028/reactions","total_count":1,"+1":1,"-1":0,"laugh":0,"hooray":0,"confused":0,"heart":0,"rocket":0,"eyes":0},"mentions_count":3},{"url":"https://api.github.com/repos/openziti/ziti/releases/307877742","assets_url":"https://api.github.com/repos/openziti/ziti/releases/307877742/assets","upload_url":"https://uploads.github.com/repos/openziti/ziti/releases/307877742/assets{?name,label}","html_url":"https://github.com/openziti/ziti/releases/tag/v1.5.14","id":307877742,"author":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"node_id":"RE_kwDODVFMN84SWddu","tag_name":"v1.5.14","target_commitish":"main","name":"v1.5.14","draft":false,"immutable":false,"prerelease":false,"created_at":"2026-04-11T15:47:59Z","updated_at":"2026-04-11T17:26:13Z","published_at":"2026-04-11T15:59:33Z","assets":[{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/394167516","id":394167516,"node_id":"RA_kwDODVFMN84XfoTc","name":"checksums.sha256.txt","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"text/plain; charset=utf-8","state":"uploaded","size":758,"digest":"sha256:7df9444777d9e1c0bc5cfe39e08de750bf44b2339001fc66df1f08afd884d0bc","download_count":5,"created_at":"2026-04-11T15:59:31Z","updated_at":"2026-04-11T15:59:31Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.5.14/checksums.sha256.txt"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/394167504","id":394167504,"node_id":"RA_kwDODVFMN84XfoTQ","name":"sbom-v1.5.14.spdx.json","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/json","state":"uploaded","size":1007075,"digest":"sha256:954a754ee72b2b72e36fcfceae012c5b275d5bcf9755286c5073e07559112c2f","download_count":3,"created_at":"2026-04-11T15:59:31Z","updated_at":"2026-04-11T15:59:31Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.5.14/sbom-v1.5.14.spdx.json"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/394167503","id":394167503,"node_id":"RA_kwDODVFMN84XfoTP","name":"source-v1.5.14.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":3401642,"digest":"sha256:cd02c673e7232507bad25e9ddd42437a333487592594d0ce7e50505505bd3472","download_count":5,"created_at":"2026-04-11T15:59:31Z","updated_at":"2026-04-11T15:59:31Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.5.14/source-v1.5.14.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/394167458","id":394167458,"node_id":"RA_kwDODVFMN84XfoSi","name":"ziti-darwin-amd64-1.5.14.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":50291709,"digest":"sha256:f1c2187b18350574302af1650369606bc6201f50fba287278fbb0b851bd4d4cc","download_count":5,"created_at":"2026-04-11T15:59:26Z","updated_at":"2026-04-11T15:59:30Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.5.14/ziti-darwin-amd64-1.5.14.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/394167463","id":394167463,"node_id":"RA_kwDODVFMN84XfoSn","name":"ziti-darwin-arm64-1.5.14.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":46779060,"digest":"sha256:eb7b86c923fff3a79bfa92c453288d4946ca5f75be5ba501243228cce44befd9","download_count":8,"created_at":"2026-04-11T15:59:26Z","updated_at":"2026-04-11T15:59:30Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.5.14/ziti-darwin-arm64-1.5.14.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/394167462","id":394167462,"node_id":"RA_kwDODVFMN84XfoSm","name":"ziti-linux-amd64-1.5.14.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":51521616,"digest":"sha256:f1b2a1c538b81f816ab2f9814a22b16f74a77c51e2874955125f30c1fbead32a","download_count":59,"created_at":"2026-04-11T15:59:26Z","updated_at":"2026-04-11T15:59:31Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.5.14/ziti-linux-amd64-1.5.14.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/394167461","id":394167461,"node_id":"RA_kwDODVFMN84XfoSl","name":"ziti-linux-arm-1.5.14.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":48149677,"digest":"sha256:d768c41bd4e0c38f16b866e8a449542563bd1f924368771d11a76004f9026736","download_count":6,"created_at":"2026-04-11T15:59:26Z","updated_at":"2026-04-11T15:59:30Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.5.14/ziti-linux-arm-1.5.14.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/394167460","id":394167460,"node_id":"RA_kwDODVFMN84XfoSk","name":"ziti-linux-arm64-1.5.14.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":48340494,"digest":"sha256:30adc1869f74a959ef1b1d48ea7d65c4d2170597fc5fa9d3ee8cb4d74aa702d5","download_count":7,"created_at":"2026-04-11T15:59:26Z","updated_at":"2026-04-11T15:59:31Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.5.14/ziti-linux-arm64-1.5.14.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/394167502","id":394167502,"node_id":"RA_kwDODVFMN84XfoTO","name":"ziti-windows-amd64-1.5.14.zip","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/zip","state":"uploaded","size":41424319,"digest":"sha256:3a849bb2923fe1d0e903db9b956f80940247300a33c0e9c743b8062da79cebbd","download_count":12,"created_at":"2026-04-11T15:59:30Z","updated_at":"2026-04-11T15:59:33Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.5.14/ziti-windows-amd64-1.5.14.zip"}],"tarball_url":"https://api.github.com/repos/openziti/ziti/tarball/v1.5.14","zipball_url":"https://api.github.com/repos/openziti/ziti/zipball/v1.5.14","body":"# Release 1.5.14\r\n\r\n## What's New\r\n\r\nUpdate libraries and build with the latest Go version.\r\n\r\n"},{"url":"https://api.github.com/repos/openziti/ziti/releases/305829194","assets_url":"https://api.github.com/repos/openziti/ziti/releases/305829194/assets","upload_url":"https://uploads.github.com/repos/openziti/ziti/releases/305829194/assets{?name,label}","html_url":"https://github.com/openziti/ziti/releases/tag/v1.6.15","id":305829194,"author":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"node_id":"RE_kwDODVFMN84SOpVK","tag_name":"v1.6.15","target_commitish":"main","name":"v1.6.15","draft":false,"immutable":false,"prerelease":false,"created_at":"2026-04-06T21:02:24Z","updated_at":"2026-04-15T13:20:25Z","published_at":"2026-04-06T21:14:18Z","assets":[{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/390308175","id":390308175,"node_id":"RA_kwDODVFMN84XQ6FP","name":"checksums.sha256.txt","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"text/plain; charset=utf-8","state":"uploaded","size":758,"digest":"sha256:6ab82bea2a070ee0e3b9012aa6a44cd72ad3493b03ae5ccd9da8b9048b7bc89b","download_count":16,"created_at":"2026-04-06T21:14:14Z","updated_at":"2026-04-06T21:14:15Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.6.15/checksums.sha256.txt"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/390308174","id":390308174,"node_id":"RA_kwDODVFMN84XQ6FO","name":"sbom-v1.6.15.spdx.json","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/json","state":"uploaded","size":1006383,"digest":"sha256:eeb93e4cbc3aaf646eb464b584fb0d5f22842c09724d2b1f2085d353e760b26c","download_count":6,"created_at":"2026-04-06T21:14:14Z","updated_at":"2026-04-06T21:14:15Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.6.15/sbom-v1.6.15.spdx.json"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/390308176","id":390308176,"node_id":"RA_kwDODVFMN84XQ6FQ","name":"source-v1.6.15.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":3506497,"digest":"sha256:daf3f786f034280a29183f6b0febaf3b7d585e7f8e02854d5cbdd531a7b37504","download_count":27,"created_at":"2026-04-06T21:14:14Z","updated_at":"2026-04-06T21:14:15Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.6.15/source-v1.6.15.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/390308147","id":390308147,"node_id":"RA_kwDODVFMN84XQ6Ez","name":"ziti-darwin-amd64-1.6.15.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":51020255,"digest":"sha256:298b02658a6d1cd7dad0f6cb1067312a9c1800b0355f1f2be913f38dd2b9f689","download_count":205,"created_at":"2026-04-06T21:14:09Z","updated_at":"2026-04-06T21:14:14Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.6.15/ziti-darwin-amd64-1.6.15.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/390308146","id":390308146,"node_id":"RA_kwDODVFMN84XQ6Ey","name":"ziti-darwin-arm64-1.6.15.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":47440963,"digest":"sha256:eb413d0790fd5de9d85d611a6ae90c9c02ec0689ac28dbfdee0039519f88eb43","download_count":281,"created_at":"2026-04-06T21:14:09Z","updated_at":"2026-04-06T21:14:14Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.6.15/ziti-darwin-arm64-1.6.15.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/390308145","id":390308145,"node_id":"RA_kwDODVFMN84XQ6Ex","name":"ziti-linux-amd64-1.6.15.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":52265478,"digest":"sha256:5c52d73d42ac7051686077ec73a150b2c7e9cce78aebeb41b39ee14ee94f1d1e","download_count":1274,"created_at":"2026-04-06T21:14:09Z","updated_at":"2026-04-06T21:14:14Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.6.15/ziti-linux-amd64-1.6.15.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/390308148","id":390308148,"node_id":"RA_kwDODVFMN84XQ6E0","name":"ziti-linux-arm-1.6.15.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":48863367,"digest":"sha256:60eeab94bdf0658af6230762be6457925f3c6a1462fb8dd9b15c94cda8df2172","download_count":12,"created_at":"2026-04-06T21:14:09Z","updated_at":"2026-04-06T21:14:14Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.6.15/ziti-linux-arm-1.6.15.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/390308173","id":390308173,"node_id":"RA_kwDODVFMN84XQ6FN","name":"ziti-linux-arm64-1.6.15.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":49024917,"digest":"sha256:f004816086d98260b66f3d4b8f9a2e86af3b38eb49b4a59292adbe1582433996","download_count":72,"created_at":"2026-04-06T21:14:14Z","updated_at":"2026-04-06T21:14:18Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.6.15/ziti-linux-arm64-1.6.15.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/390308149","id":390308149,"node_id":"RA_kwDODVFMN84XQ6E1","name":"ziti-windows-amd64-1.6.15.zip","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/zip","state":"uploaded","size":42210710,"digest":"sha256:d8b8dd839b43962c1c84145e45bd9ee9790af10c5a25141470f42a441c195f3a","download_count":612,"created_at":"2026-04-06T21:14:09Z","updated_at":"2026-04-06T21:14:14Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.6.15/ziti-windows-amd64-1.6.15.zip"}],"tarball_url":"https://api.github.com/repos/openziti/ziti/tarball/v1.6.15","zipball_url":"https://api.github.com/repos/openziti/ziti/zipball/v1.6.15","body":"# Release 1.6.15\r\n\r\n## What's New\r\n\r\n* Bug fixes and dependency updates\r\n\r\n## Component Updates and Bug Fixes\r\n\r\n* github.com/openziti/ziti: [v1.6.14 -> v1.6.15](https://github.com/openziti/ziti/compare/v1.6.14...v1.6.15)\r\n    * [Issue #3771](https://github.com/openziti/ziti/issues/3771) - [Backport-1.6] Adaptive rate limiter should use success rate rather than queue position\r\n    * [Issue #3770](https://github.com/openziti/ziti/issues/3770) - Update Token Requests Should Close Channel Connections If Invalid\r\n    * [Issue #3764](https://github.com/openziti/ziti/issues/3764) - [Backport-1.6] coalesce OIDC JWT revocations to reduce controller write pressure\r\n    * [Issue #3760](https://github.com/openziti/ziti/issues/3760) - [Backport-1.6] revocation build-up in db and rdm\r\n\r\n\r\n"},{"url":"https://api.github.com/repos/openziti/ziti/releases/305139245","assets_url":"https://api.github.com/repos/openziti/ziti/releases/305139245/assets","upload_url":"https://uploads.github.com/repos/openziti/ziti/releases/305139245/assets{?name,label}","html_url":"https://github.com/openziti/ziti/releases/tag/v1.5.13","id":305139245,"author":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"node_id":"RE_kwDODVFMN84SMA4t","tag_name":"v1.5.13","target_commitish":"main","name":"v1.5.13","draft":false,"immutable":false,"prerelease":false,"created_at":"2026-04-03T19:33:09Z","updated_at":"2026-04-03T20:54:14Z","published_at":"2026-04-03T19:44:23Z","assets":[{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/388155511","id":388155511,"node_id":"RA_kwDODVFMN84XIsh3","name":"checksums.sha256.txt","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"text/plain; charset=utf-8","state":"uploaded","size":758,"digest":"sha256:62ac862190e9ec98d8c50f2aaa8dd2dadf5442164f789e1e656def9124df9ab7","download_count":5,"created_at":"2026-04-03T19:44:21Z","updated_at":"2026-04-03T19:44:21Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.5.13/checksums.sha256.txt"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/388155507","id":388155507,"node_id":"RA_kwDODVFMN84XIshz","name":"sbom-v1.5.13.spdx.json","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/json","state":"uploaded","size":1006383,"digest":"sha256:de40fd7bafd14714739eb587c087563c3473bf409b42307f952438ce3ad70028","download_count":3,"created_at":"2026-04-03T19:44:21Z","updated_at":"2026-04-03T19:44:21Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.5.13/sbom-v1.5.13.spdx.json"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/388155508","id":388155508,"node_id":"RA_kwDODVFMN84XIsh0","name":"source-v1.5.13.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":3401699,"digest":"sha256:eebb5a33d638d2948ed3a680dca140206482c0957c1d8e91c4139328d59e755e","download_count":5,"created_at":"2026-04-03T19:44:21Z","updated_at":"2026-04-03T19:44:21Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.5.13/source-v1.5.13.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/388155500","id":388155500,"node_id":"RA_kwDODVFMN84XIshs","name":"ziti-darwin-amd64-1.5.13.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":50277168,"digest":"sha256:ca80f26daa5053e87b318f1c18ac877a1602d11c3fcaf9f1c1ee1495ecadf9aa","download_count":5,"created_at":"2026-04-03T19:44:21Z","updated_at":"2026-04-03T19:44:22Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.5.13/ziti-darwin-amd64-1.5.13.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/388155432","id":388155432,"node_id":"RA_kwDODVFMN84XIsgo","name":"ziti-darwin-arm64-1.5.13.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":46781213,"digest":"sha256:56293d6e23f7cd8cce688d1d3c4fc8a9a11865d8d45802847ba82d8bb6605bff","download_count":10,"created_at":"2026-04-03T19:44:19Z","updated_at":"2026-04-03T19:44:21Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.5.13/ziti-darwin-arm64-1.5.13.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/388155434","id":388155434,"node_id":"RA_kwDODVFMN84XIsgq","name":"ziti-linux-amd64-1.5.13.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":51492089,"digest":"sha256:3489cc3abc82d7ee31bf95885a1238900385745960e146dd88e43cadedfca508","download_count":22,"created_at":"2026-04-03T19:44:19Z","updated_at":"2026-04-03T19:44:21Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.5.13/ziti-linux-amd64-1.5.13.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/388155436","id":388155436,"node_id":"RA_kwDODVFMN84XIsgs","name":"ziti-linux-arm-1.5.13.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":48141184,"digest":"sha256:25feec982b757c0ad39105a5d686b00a67e4f58631c3c8156323b0d52ad1b975","download_count":5,"created_at":"2026-04-03T19:44:19Z","updated_at":"2026-04-03T19:44:21Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.5.13/ziti-linux-arm-1.5.13.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/388155435","id":388155435,"node_id":"RA_kwDODVFMN84XIsgr","name":"ziti-linux-arm64-1.5.13.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":48316715,"digest":"sha256:e400bdd03c69523ba1a2496b050269601ac1de76b2ea78354111dc5804e10c8d","download_count":7,"created_at":"2026-04-03T19:44:19Z","updated_at":"2026-04-03T19:44:21Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.5.13/ziti-linux-arm64-1.5.13.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/388155437","id":388155437,"node_id":"RA_kwDODVFMN84XIsgt","name":"ziti-windows-amd64-1.5.13.zip","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/zip","state":"uploaded","size":41434917,"digest":"sha256:41f3eaa535561eb6f825f05c62ab7728f30a10e7b81a5889e870a0ed6e76605e","download_count":9,"created_at":"2026-04-03T19:44:19Z","updated_at":"2026-04-03T19:44:20Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.5.13/ziti-windows-amd64-1.5.13.zip"}],"tarball_url":"https://api.github.com/repos/openziti/ziti/tarball/v1.5.13","zipball_url":"https://api.github.com/repos/openziti/ziti/zipball/v1.5.13","body":"# Release 1.5.13\r\n\r\n## What's New\r\n\r\nUpdate libraries and build with the latest Go version.\r\n\r\n"},{"url":"https://api.github.com/repos/openziti/ziti/releases/303521995","assets_url":"https://api.github.com/repos/openziti/ziti/releases/303521995/assets","upload_url":"https://uploads.github.com/repos/openziti/ziti/releases/303521995/assets{?name,label}","html_url":"https://github.com/openziti/ziti/releases/tag/v2.0.0-pre10","id":303521995,"author":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"node_id":"RE_kwDODVFMN84SF2DL","tag_name":"v2.0.0-pre10","target_commitish":"main","name":"v2.0.0-pre10","draft":false,"immutable":false,"prerelease":true,"created_at":"2026-03-31T05:16:30Z","updated_at":"2026-03-31T05:21:34Z","published_at":"2026-03-31T05:21:34Z","assets":[{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/385302695","id":385302695,"node_id":"RA_kwDODVFMN84W90Cn","name":"checksums.sha256.txt","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"text/plain; charset=utf-8","state":"uploaded","size":798,"digest":"sha256:1f34235146ae4cbc353eb1d1b4651d88b84d39f66f11d74b25e0b61d69ee8b84","download_count":5,"created_at":"2026-03-31T05:21:31Z","updated_at":"2026-03-31T05:21:32Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre10/checksums.sha256.txt"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/385302693","id":385302693,"node_id":"RA_kwDODVFMN84W90Cl","name":"sbom-v2.0.0-pre10.spdx.json","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/json","state":"uploaded","size":1008832,"digest":"sha256:9d92a4b72591b5b14975b055a0f3d8966a9546aa5f9c5756195f11ffa1ae9aff","download_count":3,"created_at":"2026-03-31T05:21:31Z","updated_at":"2026-03-31T05:21:32Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre10/sbom-v2.0.0-pre10.spdx.json"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/385302694","id":385302694,"node_id":"RA_kwDODVFMN84W90Cm","name":"source-v2.0.0-pre10.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":3787968,"digest":"sha256:37a428bfc9d414010336a76f1994f74054eb0e1ba010262ce8db3b9c9cefffae","download_count":5,"created_at":"2026-03-31T05:21:31Z","updated_at":"2026-03-31T05:21:32Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre10/source-v2.0.0-pre10.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/385302672","id":385302672,"node_id":"RA_kwDODVFMN84W90CQ","name":"ziti-darwin-amd64-2.0.0-pre10.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":53975018,"digest":"sha256:3a2133c8f391a68b19a5c4ad618815fe7f569a214077be5b6b1e9d1be28a70d0","download_count":8,"created_at":"2026-03-31T05:21:28Z","updated_at":"2026-03-31T05:21:31Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre10/ziti-darwin-amd64-2.0.0-pre10.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/385302670","id":385302670,"node_id":"RA_kwDODVFMN84W90CO","name":"ziti-darwin-arm64-2.0.0-pre10.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":50325741,"digest":"sha256:75d6d061a7dd7bc88b7ddbfeab69c7cdc9354a507126b567e87d21e7811e7434","download_count":8,"created_at":"2026-03-31T05:21:27Z","updated_at":"2026-03-31T05:21:31Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre10/ziti-darwin-arm64-2.0.0-pre10.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/385302673","id":385302673,"node_id":"RA_kwDODVFMN84W90CR","name":"ziti-linux-amd64-2.0.0-pre10.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":55272730,"digest":"sha256:aa442632c5b4241bbc0b35e92507f715004c0588c183b6438884d454a8d4a84f","download_count":279,"created_at":"2026-03-31T05:21:28Z","updated_at":"2026-03-31T05:21:31Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre10/ziti-linux-amd64-2.0.0-pre10.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/385302668","id":385302668,"node_id":"RA_kwDODVFMN84W90CM","name":"ziti-linux-arm-2.0.0-pre10.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":51776420,"digest":"sha256:0ca8d55da27f9ce4a86effba266c63a0a992c0831d0b8be39f2e6aaf31ece95d","download_count":7,"created_at":"2026-03-31T05:21:27Z","updated_at":"2026-03-31T05:21:31Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre10/ziti-linux-arm-2.0.0-pre10.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/385302669","id":385302669,"node_id":"RA_kwDODVFMN84W90CN","name":"ziti-linux-arm64-2.0.0-pre10.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":51789776,"digest":"sha256:fede30282865c7a23adcb0d17ba95514db18d5aa69d26bdcf21ce542a65beab9","download_count":14,"created_at":"2026-03-31T05:21:27Z","updated_at":"2026-03-31T05:21:31Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre10/ziti-linux-arm64-2.0.0-pre10.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/385302692","id":385302692,"node_id":"RA_kwDODVFMN84W90Ck","name":"ziti-windows-amd64-2.0.0-pre10.zip","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/zip","state":"uploaded","size":44656810,"digest":"sha256:45cfcc4ce73764ef22ad153321acdec41d4aece0f6e89aead4efc33014918a14","download_count":7,"created_at":"2026-03-31T05:21:31Z","updated_at":"2026-03-31T05:21:33Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre10/ziti-windows-amd64-2.0.0-pre10.zip"}],"tarball_url":"https://api.github.com/repos/openziti/ziti/tarball/v2.0.0-pre10","zipball_url":"https://api.github.com/repos/openziti/ziti/zipball/v2.0.0-pre10","body":"# Release 2.0.0\n\n## What's New\n\nThis is the next major version release of OpenZiti, following the 1.0 release in April 2024.\nOf particular note is that HA controllers are now considered ready for general use.\nThis release also introduces a new permissions model, OIDC/JWT token-based enrollment, \nclustering performance improvements, and a number of other features and fixes. Because \nsome of these changes are not backwards compatible with older routers, we're marking this \nas a major version bump.\n\n### HA Controllers are now considered ready for general use\n\nThis is a pretty big milestone and marks the completion of work that's been ongoing for a couple of years.\nThe HA work has brought with it some notable changes. Authentication now uses JWTs by default. Using JWTs\nmeans the controllers don't need to store session and propagate them to the routers. This removes a bottleneck\nfrom the network and allows the load to be more easily distributed among controllers and routers.\n\nTo support distributed authentication, the routers now get a bespoke version of the data model. In addition\nto enabling distributed authentication this will allow us to remove the need for service polling and further \nreduce the load on controllers in the future.\n\n### Router Compatibility\n\nRelated to the JWT work, routers with version 2.+ will only work with controllers that are version 2.+. This means\nto upgrade your network, controllers should be upgraded first. Routers can then be upgraded individually.\n\n2.x routers should still work fine with older router versions.\n\nWe try very hard to avoid breaking changes like this, but sometimes the engineering trade-offs lead there. This change\nwas first made in the 1.7 release. That release has not been marked stable, and we have no plans to do so, because\nof the backwards incompatibility. \n\nIf you need to support in the 1.6.12+ range, see the section \"OIDC enabled by default\".\n\n### New Permissions Model (BETA)\n\nAs one feature goes out of beta, another arrives into beta. This release introduces a new permissions system\nfor more fine grained control to the management API. It's not expected to change, but may do so based on feedback\nfrom users.\n\n### Updated Release Process\n\nWe have moved to creating `-preN` releases for major and minor versions. This way we can put out release candidates,\nor feature previews and put them through internal testing and let interested folks from the community try them out.\nThen, when we're ready, we can run the full validation suite against the last pre-release and retag it. \n\nPatch releases won't have `-preN` and should contain only high priority bug fixes.\n\n### Deprecation Cleanup\n\nSince we already have a breaking change, we're removing some other backwards compatibility code.\n\n* Controller managed links \n    * Router managed links were introduced in v0.30.0. \n    * If you're upgrading from an older versions, you'll want to upgrade to the latest 1.x release before jumping to 2.x\n    * Github tracking issue: https://github.com/openziti/ziti/issues/3512\n* `ziti edge create identity <type>`\n    * Identity types other than router were removed in v0.30.2\n    * The `type` can be dropped from the CLI command\n    * Github tracking issue: https://github.com/openziti/ziti/issues/3532\n* Terminator create/update/delete events\n    * These have been superseded by entity change events, which also have create/update/delete events for terminators\n    * Entity change events were introduced in v0.28.0\n    * Github tracking issue: https://github.com/openziti/ziti/issues/3531\n* `xgress_edge_tunnel` v1\n    * This is the first implementation of the tunneler in edge-router code (ER/T) which used legacy api sessions and services\n    * The v2 version uses the router data model and was introduced in v0.30.x\n    * Github tracking issue: https://github.com/openziti/ziti/issues/3516\n\n### Legacy Session Deprecation\n\nOIDC sessions are now preferred. They are the default, or will become the default for SDKs and tunnelers. They are also required\nwhen running HA. Legacy API and service session are now deprecated and will be removed in the OpenZiti v3.0.0 release. \n\n### Additional Features\n\n* Controllers can now optionally bind APIs using an OpenZiti identity\n* `ziti edge login` now supports the `--network-identity` flag to authenticate and establish connections through the Ziti overlay network\n* `ziti edge login` now supports using a bearer token with `--token` for authentication. The token is expected to be\n  provided as just the JWT, not with the \"Bearer \" prefix\n* Identity configuration can now be loaded from files or environment variables for flexible deployment scenarios\n* Identities can now be provisioned just-in-time through OIDC/JWT token-based enrollment\n* Multiple model updates can now be in-flight at the same time, improving clustering performance\n* Authentication-related model updates can now be non-blocking and even dropped if the system is too busy\n* Routers now provide more error context to SDKs for terminator errors, enabling better retry behavior\n* New `proxy.v1` config type for dynamic service proxies (originally released in 1.7.0)\n* New alert event type for surfacing operational issues to network operators - Beta (originally released in 1.7.0)\n* New Azure Service Bus event sink for streaming controller events, contributed by @ffaraone (originally released in 1.7.0)\n* Bundled ZAC upgraded to 4.0\n* Build updated to Go 1.25\n* CLI cleaned up to remove calls to `os.Exit`, making it more friendly for embedding\n* OIDC is now enabled by default\n* Controller Edge APIs now return `WWW-Authenticate` response headers on `401 Unauthorized` responses, giving clients actionable information about which auth methods are accepted and what went wrong\n* HA Controllers can be marked as 'preferredLeader' via config\n* Dynamic cost range for smart routing expanded beyond the previous 64K limit\n* Dial failures now return the circuit ID and error information for easier debugging\n* Router-to-controller control channels now support multiple underlays with priority-based message routing\n* The dialing identity's ID and name are now forwarded to the hosting SDK\n* Controllers can now dial routers to establish control channels, enabling connectivity when routers are behind firewalls (Beta)\n* Refresh-token revocations are now batched and best-effort, removing the database/raft bottleneck on token refreshes\n* `ziti edge quickstart` now always runs in HA mode. The `ha` subcommand has been removed. Use\n  `ziti edge quickstart join` to add additional members to the cluster. Note: existing quickstart instances\n  are not compatible with the new HA-only mode and will need to be recreated.\n* The `--clustered` flag on `ziti create config controller` has been removed; the generated config is always\n  cluster-ready. If you have scripts passing `--clustered`, remove it.\n\n## Basic Permission System (BETA)\n\nAdded a basic permission system that allows more control over identity access to controller management API operations. \nThis replaces the previous binary admin/non-admin model with a more flexible permission system.\n\n**NOTE:** This feature is in BETA, primarily so we can get feedback on which permissions make sense. The implementation is unlikely to change\nbut the set of exposed permissions may grow, shrink or change based on user feedback. \n\n### Permission Model\n\nThe permission system supports three levels of authorization:\n\n  1. **Global Permissions**: System-wide access levels\n     - `admin` - Full access to all operations. This is still controlled by the `isAdmin` flag on identity\n     - `admin_readonly` - Read-only access to all resources except debugging facilities inspect and validate\n\n  2. **Entity-Level Permissions**: Full CRUD access to specific entity types\n     - Granting an entity-level permission (e.g., `service`) provides complete create, read, update, and delete access for that entity type\n\n  3. **Action-Level Permissions**: Specific operation access on entity types\n     - Fine-grained control using the pattern `<entity>.<action>` (e.g., `service.read`, `identity.update`)\n     - Supports `create`, `read`, `update`, and `delete` actions per entity type\n\n### Supported Entity Permissions\n\nThe following entity-level permissions are available:\n\n- `auth-policy` - Authentication policy management\n- `ca` - Certificate Authority management\n- `config` - Configuration management\n- `config-type` - Configuration type management\n- `edge-router-policy` - Edge router policy management\n- `enrollment` - Enrollment management\n- `external-jwt-signer` - External JWT signer management\n- `identity` - Identity management\n- `posture-check` - Posture check management\n- `router` - Edge and transit router management\n- `service` - Service management\n- `service-policy` - Service policy management\n- `service-edge-router-policy` - Service edge router policy management\n- `terminator` - Terminator management\n- `ops` - Operational resources (API sessions, sessions, circuits, links, inspect and validate)\n\n### Permission Assignment\n\nPermissions are assigned to identities via the `permissions` field in the identity resource. Multiple permissions can be granted to a single identity, and permissions are additive.\n\n### Cross-Entity Operations\n\nListing related entities through an entity's endpoints requires appropriate permissions for the related entity type. For example:\n- Listing services for a service-policy requires `service.read` permission\n- Listing identities for an edge-router-policy requires `identity.read` permission\n- Listing configs for a service requires `config.read` permission\n\n**NOTE:** \nMore permissions than expected may be required when performing actions through the CLI or ZAC. Take for example, when an identity \nhas `config.create` and is attempting to create a new config. The CLI may fail if the identity doesn't have `config-type.read`\nas well because it will need to look up the config type id that corresponds to the given config type name.\n\nSimilar cross entity read permissions may be required when creating services.\n\n### Admin Protection\n\nNon-admin identities cannot:\n- Create identities with the `isAdmin` flag\n- Create identities with any permissions granted\n- Modify admin-related fields on existing identities\n- Update or delete admin identities\n- Grant permissions to identities\n\nThese protections ensure that privilege escalation is prevented and admin access remains controlled.\n\n\n## Binding Controller APIs With Identity\n\nController APIs can now be bound to an OpenZiti overlay network identity, allowing secure communication through\nthe Ziti network. This is useful for scenarios where you want to expose controller APIs only through the overlay\nnetwork rather than on a standard network interface.\n\n### Configuration Structure\n\nA standard `bindPoint` configuration looks like this:\n```text\n    bindPoints:\n      - interface: 127.0.0.1:18441\n        address: 127.0.0.1:18441\n```\n\nTo bind controller APIs to an OpenZiti identity, add an additional `identity` block to your `bindPoints`. The\nidentity configuration specifies where to load the Ziti identity file and which service to bind it to:\n\n```text\n    bindPoints:\n      - interface: 127.0.0.1:18441\n        address: 127.0.0.1:18441\n      - identity:\n          file: \"c:/temp/ctrl.testing/ctrl.identity.json\"\n          service: \"mgmt\"\n```\n\n### Supported Configuration Options\n\n- `file`: Path to a Ziti identity JSON file containing the controller's identity and enrollment certificate\n- `env`: Name of an environment variable containing a base64-encoded Ziti identity (alternative to `file`)\n- `service`: The name of the Ziti service to bind the controller API to\n\n### Using Environment Variables\n\nFor deployments where storing identity files on disk is not preferred, you can reference a base64-encoded\nidentity file from an environment variable. The environment variable should contain the base64-encoded contents\nof the identity JSON file.\n\nFor example, if an environment variable named `ZITI_CTRL_IDENTITY` contains a base64-encoded identity file:\n\n```text\n    bindPoints:\n      - interface: 127.0.0.1:18441\n        address: 127.0.0.1:18441\n      - identity:\n          env: ZITI_CTRL_IDENTITY\n          service: \"mgmt\"\n```\n\n### IPv6 Support\n\nBoth IPv4 and IPv6 addresses are supported for standard bind points. IPv6 addresses should be specified in bracket\nnotation with a port number:\n\n```text\n    bindPoints:\n      - interface: \"[::1]:18441\"\n        address: \"[::1]:18441\"\n      - identity:\n          file: \"/path/to/identity.json\"\n          service: \"mgmt\"\n```\n\n## CLI Enhancements for Identity-Based Connections\n\nThe `ziti edge login` command and REST client utilities have been enhanced to support identity-based connections\nthrough the Ziti overlay network.\n\n### New `--network-identity` Flag for `ziti edge login`\n\nThe `ziti edge login` command now includes a `--network-identity` flag that allows you to authenticate to a Ziti\ncontroller through the overlay network using a Ziti identity:\n\n```bash\nziti edge login https://ziti.mgmt.apis.local:1280 \\\n  --username myuser \\\n  --password mypass \\\n  --network-identity /path/to/identity.json\n```\n\nThis is useful when the controller is only accessible through the Ziti overlay network or when you want to ensure\nall communication to the controller flows through the overlay for security purposes.\n\n### Identity Resolution Order\n\nWhen establishing connections, identities are resolved in the following order:\n\n1. **Command-line flag**: The `--network-identity` flag takes precedence\n2. **Environment variable**: If `ZITI_CLI_NETWORK_ID` is set and contains a base64-encoded identity, it is used\n3. **Cached identity file**: If a network identity was saved from a previous login in the Ziti config directory, it may be used\n\nThis layered approach allows for flexibility in deployment scenarios:\n- Development: Use command-line flags for quick testing\n- Automation: Use environment variables in CI/CD pipelines\n- Production: Cache identities securely for repeated access\n\n#### Dialing Modes When Authenticating\n\nThe CLI supports two dialing modes:\n\n**Intercept-based Dialing (Default)**\nBy default, URLs are expected to leverage intercepts. Create a service with an appropriate intercept config and use\nthe intercept address when dialing. This is the standard mode for most use cases. For example, given a service with\nthe intercept `ziti.mgmt.apis.local`\n```bash\nziti edge login https://ziti.mgmt.apis.local:1280 \\\n  --username myuser \\\n  --password mypass \\\n  --network-identity /path/to/identity.json\n```\n\n**Identity-aware Dialing (Addressable Terminators)**\nTo support addressable terminators-based dialing, specify a user in the URL. This activates dial-by-identity\nfunctionality. The URL format should be `identity-to-dial@service-name-to-dial`. For example:\n```bash\nziti edge login https://my-identity@my-service:1280 \\\n  --username myuser \\\n  --password mypass \\\n  --network-identity /path/to/identity.json\n```\n\nIn this mode, the transport extracts the identity from the URL and uses it to establish a direct connection to\nthe specified service via the addressable terminator.\n\n\n## OIDC/JWT Token-based Enrollment\n\nOpenZiti now supports provisioning identities just-in-time through OIDC/JWT token enrollment. External identity \nproviders can be configured to allow identities to enroll using JWT tokens, with support for the resulting \nidentities to use certificate or token authentication.\n\n### External JWT Signer Configuration\n\nExternal JWT signers are configured via the Edge Management API to define enrollment behavior with the following new \nenrollment-specific properties:\n\n- **enrollToCertEnabled** - When enabled, identities can exchange a JWT token and a certificate signing request (CSR) \n        for a client certificate during enrollment. The certificate can then be used for standard certificate-based\n        authentication.\n\n- **enrollToTokenEnabled** - When enabled, identities can use a JWT token to enroll. The current token or future tokens\n        may be used for authentication.\n\n- **enrollNameClaimsSelector** - Specifies which JWT claim contains the identity name. Accepts a JSON pointer \n        (e.g., `/preferred_username`) or a simple property name (e.g., `preferred_username`, automatically converted to\n        `/preferred_username`). Defaults to `/sub` if not specified. The extracted value becomes the identity name in Ziti.\n\n- **enrollAttributeClaimsSelector** - Specifies which JWT claims to extract as identity attributes during enrollment.\n        Accepts a JSON pointer (e.g., `/roles`) or a simple property name (e.g., `roles`). Extracted attributes are \n        applied to the newly enrolled identity for use in authorization policies.\n\n- **enrollAuthPolicyId** - Specifies the authentication policy to apply to newly enrolled identities. This determines\n        what authentication methods are available for the identity post-enrollment.\n\nAdditionally the existing property named **claimsProperty** that specifies external id to match identities to:\n\n- now supports a JSON pointer (e.g., `/id`) or a simple property name (e.g., `id`)\n- is used to populate the `externalId` field of the identity\n\n### Enrollment Paths\n\n#### Certificate Enrollment (enrollToCertEnabled)\n\nWhen certificate enrollment is enabled, unauthenticated users can:\n\n1. Obtain a list of available IdPs from the public Edge Client API `GET /external-jwt-signers` endpoint, where \n   `enrollToCertEnabled` is set to `true`\n2. Obtain a JWT from the configured OIDC provider\n3. Generate a certificate signing request (CSR)\n4. Submit an enrollment request with the JWT and CSR\n5. Have their identity created in Ziti with attributes extracted from JWT claims\n6. Receive a signed client certificate for certificate-based authentication\n\n#### Token Enrollment (enrollToTokenEnabled)\n\nWhen token enrollment is enabled, unauthenticated users can:\n\n1. Obtain a list of available IdPs from the public Edge Client API `GET /external-jwt-signers` endpoint, where \n   `enrollToTokenEnabled` is set to `true`\n1. Obtain a JWT from the configured OIDC provider\n2. Submit an enrollment request with the JWT\n3. Have their identity created in Ziti with attributes extracted from JWT claims\n4. Receive a Ziti API token for token-based authentication\n\n### Edge Management API\n\nThe Edge Management API provides full CRUD operations for configuring external JWT signers:\n\n- `POST /external-jwt-signers` - Create a new external JWT signer with all configuration options\n- `GET /external-jwt-signers` - List all configured external JWT signers\n- `GET /external-jwt-signers/{id}` - Retrieve a specific signer configuration\n- `PUT /external-jwt-signers/{id}` - Update all fields of a signer\n- `PATCH /external-jwt-signers/{id}` - Partially update a signer\n- `DELETE /external-jwt-signers/{id}` - Delete a signer\n\n### Edge Client API\n\nThe Edge Client API exposes a reduced set of external JWT signer information for unauthenticated enrollment requests:\n\n- `GET /external-jwt-signers` - List available JWT signers with enrollment capabilities\n\nThe client API response includes the following fields for each signer:\n\n- `name` - Signer name\n- `externalAuthUrl` - URL where users obtain JWT tokens\n- `clientId` - OIDC client ID\n- `scopes` - Requested OIDC scopes\n- `openIdConfigurationUrl` - OIDC discovery endpoint\n- `audience` - Expected token audience\n- `targetToken` - Token type to use (ACCESS or ID)\n- **`enrollToCertEnabled`** - Flag indicating certificate enrollment is available\n- **`enrollToTokenEnabled`** - Flag indicating token enrollment is available\n\n### CLI Commands\n\n**Create an external JWT signer with enrollment options:**\n```\nziti edge controller create ext-jwt-signer <name> <issuer> \\\n  --jwks-endpoint <url> \\\n  --audience <audience> \\\n  --enroll-to-cert \\\n  --enroll-to-token=false \\\n  --enroll-name-claims-selector preferred_username \\\n  --enroll-attr-claims-selector roles \\\n  --enroll-auth-policy <policy-id-or-name>\n```\n\n**Update enrollment options on an existing signer:**\n```\nziti edge controller update ext-jwt-signer <name|id> \\\n  --enroll-to-cert \\\n  --enroll-auth-policy <policy-id-or-name>\n```\n\n**List external JWT signers:**\n```\nziti edge controller list ext-jwt-signers\n```\n\n## Clustering Performance Improvements\n\nIn previous releases, model updates were submitted to raft one at at time. This prevented \nraft from being efficient by allowing command batching. This release allows multiple \nmodel updates to be in-flight at the same time. \n\nNew Configuration Options\n\n1. Raft Apply Timeout (cluster.applyTimeout)\n\nLocation: Controller configuration file, under the cluster section\nType: Duration\nDefault: 5s\nDescription: Timeout for applying commands to the Raft distributed log. Commands that exceed this timeout will trigger adaptive rate limiter backoff.\n\nExample:\n```\ncluster:\n  applyTimeout: 10s\n```\n\n2. Cluster Rate Limiter Configuration (cluster.rateLimiter)\n\nA new adaptive rate limiter that controls the submission of commands to the Raft cluster. Unlike the existing command rate limiter, this specifically manages in-flight Raft operations with adaptive window sizing.\n\nConfiguration Structure:\n```\ncluster:\n  rateLimiter:\n    enabled: true\n    minSize: 5\n    maxSize: 250\n    timeout: 30s\n```\n\nSub-options:\n\n  - enabled (boolean)\n    - Default: true\n    - Description: Enable/disable adaptive rate limiting for Raft command submission\n  - minSize (integer)\n    - Default: 5\n    - Minimum: 1\n    - Description: Minimum window size for concurrent in-flight Raft operations\n  - maxSize (integer)\n    - Default: 250\n    - Description: Maximum window size for concurrent in-flight Raft operations. Must be >= minSize\n  - timeout (duration)\n    - Default: 30s\n    - Description: Time after which outstanding work is assumed to have failed if not marked completed\n\n3. Restart Self on Snapshot (cluster.restartSelfOnSnapshot)\n\nLocation: Controller configuration file, under cluster section\nType: Boolean\nDefault: false\nDescription: When true, the controller will automatically restart itself when restoring a snapshot to an initialized system. When false, the controller will exit with code 0, requiring external process management to restart it.\n\nExample:\n```\ncluster:\n    restartSelfOnSnapshot: true\n```\n\n### New Metrics\n\nThe adaptive rate limiter exposes three new metrics:\n\n  1. raft.rate_limiter.queue_size (gauge)\n    - Current number of operations queued/in-flight\n  2. raft.rate_limiter.work_timer (timer)\n    - Duration of rate-limited operations\n  3. raft.rate_limiter.window_size (gauge)\n    - Current adaptive window size\n\n## Rate Limiter Algorithm Improvements\n\nThe adaptive rate limiter tracker now uses a success rate metric to decide when to grow or shrink its\nconcurrency window, instead of using raw queue position. An exponentially decaying histogram tracks the\nratio of successes to backoffs. When the success rate exceeds a configurable threshold, the window is\ngrown by a multiplicative increase factor. When it falls below the threshold, the window is shrunk by a\nmultiplicative decrease factor. The check intervals for increase and decrease are independently configurable.\n\nThis approach produces smoother, more stable window adjustments under varying load, avoiding the\nover-aggressive shrinking that could occur when queue position alone was used as the signal.\n\nThis specific rate limiter implementation is used in three places:\n* **Controller TLS handshake rate limiting** - controls the rate of incoming TLS handshakes on the controller\n* **Raft command submission** - controls the rate of commands submitted to the Raft distributed log\n* **Router control channel rate limiting** - controls the rate of requests from the router to the controller\n\nNote: this work was done in advance of enabling the TLS handshake rate limiter by default. The TLS\nhandshake rate limiter is not yet enabled by default, but can be enabled via the `tls.rateLimiter`\nconfiguration section.\n\nNew configuration options (available under each `rateLimiter` section):\n\n  - successThreshold (float)\n    - Default: 0.9\n    - Description: Success rate threshold above which the window size will be increased and below which it will be decreased\n  - increaseFactor (float)\n    - Default: 1.02\n    - Description: Multiplier applied to the current window size when growing. Must be greater than 1\n  - decreaseFactor (float)\n    - Default: 0.9\n    - Description: Multiplier applied to the current window size when shrinking. Must be between 0 and 1\n  - increaseCheckInterval (integer)\n    - Default: 10\n    - Description: Number of successes between window size increase checks\n  - decreaseCheckInterval (integer)\n    - Default: 10\n    - Description: Number of backoffs between window size decrease checks\n\n## Background Processing for Identity Updates\n\nIdentity environment and authenticator updates that occur during authentication are now processed asynchronously in the background. \nThis prevents authentication requests from blocking when the system is under load, significantly improving resilience during thundering herd scenarios.\n\nWhen the background queue fills up, updates can be dropped as they will be refreshed on the next authentication attempt. \nThis allows the system to gracefully handle load spikes without impacting authentication performance.\n\nFor now, dropping entries when the queue fills will be disabled by default, but can be enabled, see below.\n\n### Configuration\n\nA new `command.background` configuration section controls the background processing behavior:\n\n```yaml\n  command:\n    background:\n      enabled: true           # Enable background processing (default: true)\n      queueSize: 1000        # Maximum queue size (default: 1000)\n      dropWhenFull: true     # Drop updates when queue is full (default: false)\n      delayThreshold: 50ms   # The threshold for how long updates are taking before starting to background updates\n```\n\nNote that the `commandRateLimiter` configuration section may instead be specified under `command` as `rateLimiter`.\n\nExample:\n\n```yaml\n  command:\n    background:\n      enabled: true\n      queueSize: 250\n      dropWhenFull: false\n      delayThreshold: 50ms\n    rateLimiter:\n      enabled:   true\n      maxQueued: 25\n```\n\nNote that if command rate limiter configuration is specified in both locations, the settings under `command` will take \nprecedence. The standalone `commandRateLimiter` section may be deprecated in the future.\n\n### Metrics\n\nWhen background processing is enabled, the following metrics are exposed:\n\n- command.background.queue_size - Current number of queued background tasks\n- command.background.worker_count - Current number of worker goroutines\n- command.background.busy_workers - Number of workers currently processing tasks\n- command.background.work_timer - Timer tracking background task execution (includes histogram, meter, and count)\n- command.background.dropped_entries - Count of dropped updates when queue is full (only when dropWhenFull is enabled)\n\n## New proxy.v1 Config Type\n\n*Originally released in 1.7.0*\n\nAdded support for dynamic service proxies with configurable binding and protocol options.\nThis allows Edge Routers and Tunnelers to create proxy endpoints that can forward traffic for Ziti services.\n\nThis differs from intercept.v1 in that intercept.v1 will intercept traffic on specified\nIP addresses or DNS entries to forward to a service using tproxy or tun interface,\ndepending on implementation.\n\nA proxy on the other hand will just start a regular TCP/UDP listener on the configured port,\nso traffic will have to be configured for that destination.\n\nExample proxy.v1 Configuration:\n\n```\n  {\n    \"port\": 8080,\n    \"protocols\": [\"tcp\"],\n    \"binding\": \"0.0.0.0\"\n  }\n```\n\nConfiguration Properties:\n  - port (required): Port number to listen on (1-65535)\n  - protocols (required): Array of supported protocols (tcp, udp)\n  - binding (optional): Interface to bind to. For the ER/T defaults to the configured lanIF config property.\n\nThis config type is currently supported by the ER/T when running in either proxy or tproxy mode.\n\n## Alert Events (BETA)\n\n*Originally released in 1.7.0*\n\nA new alert event type has been added to allow Ziti components to emit alerts for issues that network operators can address.\nAlert events are generated when components encounter problems such as service configuration errors or resource\navailability issues.\n\nAlert events include:\n  - Alert source type and ID (currently supports routers, with controller and SDK support planned for future releases)\n  - Severity level (currently supports error, with info and warning planned for future releases)\n  - Alert message and supporting details\n  - Related entities (router, identity, service, etc.) associated with the alert\n\nExample alert event when a router cannot bind a configured network interface:\n\n```\n  {\n    \"namespace\": \"alert\",\n    \"event_src_id\": \"ctrl1\",\n    \"timestamp\": \"2021-11-08T14:45:45.785561479-05:00\",\n    \"alert_source_type\": \"router\",\n    \"alert_source_id\": \"DJFljCCoLs\",\n    \"severity\": \"error\",\n    \"message\": \"error starting proxy listener for service 'test'\",\n    \"details\": [\n      \"unable to bind eth0, no address\"\n    ],\n    \"related_entities\": {\n      \"router\": \"DJFljCCoLs\",\n      \"identity\": \"DJFljCCoLs\",\n      \"service\": \"3DPjxybDvXlo878CB0X2Zs\"\n    }\n  }\n```\n\nAlert events can be consumed through the standard event system and logged to configured event handlers for monitoring and alerting purposes.\n\nThese events are currently in Beta, as the format is still subject to change. Once they've been in use in production for a while\nand proven useful, they will be marked as stable.\n\n## Azure Service Bus Event Sink\n\n*Originally released in 1.7.0. Contributed by @ffaraone.*\n\nAdds support for streaming controller events to Azure Service Bus.\nThe new logger enables real-time event streaming from the OpenZiti controller to Azure Service Bus\nqueues or topics, providing integration with Azure-based monitoring and analytics systems.\n\nTo enable the Azure Service Bus event logger, add configuration to the controller config file under the events section:\n\n```\n  events:\n    serviceBusLogger:\n      subscriptions:\n        - type: circuit\n        - type: session\n        - type: metrics\n          sourceFilter: .*\n          metricFilter: .*\n        # Add other event types as needed\n      handler:\n        type: servicebus\n        format: json\n        connectionString: \"Endpoint=sb://your-namespace.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=your-key\"\n        topic: \"ziti-events\"          # Use 'topic' for Service Bus topic\n        # queue: \"ziti-events-queue\"  # Or use 'queue' for Service Bus queue\n        bufferSize: 100                # Optional, defaults to 50\n```\n\n- Required configuration:\n    - format: Event format, currently supports only json\n    - connectionString: Azure Service Bus connection string\n    - Either topic or queue: Destination name (mutually exclusive)\n\n- Optional configuration:\n    - bufferSize: Internal message buffer size (default: 50)\n\n## OIDC is now enabled by default\n\nThe controller now automatically adds the `edge-oidc` API binding to any web listener that hosts\nthe `edge-client` API, even when `edge-oidc` is not explicitly listed in the `web` section of the\nconfiguration. This means OIDC-based authentication is available out of the box without requiring\nchanges to existing controller configurations.\n\n### Where OIDC binds\n\nThe `edge-oidc` binding is added to the same web listener(s) as `edge-client`. If `edge-client` is\nhosted on `127.0.0.1:1280`, the OIDC endpoints will be available on that same address under the\n`/oidc` path (e.g. `https://127.0.0.1:1280/oidc/.well-known/openid-configuration`).\n\nThe controller capabilities returned by `GET /version` will include `OIDC_AUTH` and the\n`edge-oidc` entry will be present in `apiVersions` when OIDC is active.\n\n### Opting out\n\nIf OIDC should not be enabled automatically, set `disableOidcAutoBinding: true` under `edge.api`:\n\n```yaml\nedge:\n  api:\n    address: 127.0.0.1:1280\n    sessionTimeout: 30m\n    disableOidcAutoBinding: true\n```\n\nWhen `disableOidcAutoBinding` is `true`, OIDC will only be active if `edge-oidc` is explicitly\nlisted as a binding in the `web` section. \n\nWhen OIDC is not enabled, all (SDK) clients will revert to using legacy authentication.\nLegacy authentication does not support multiple controllers or HA in general. Clients will treat\ntheir controller as if it is a single controller instance and will function as if no other controllers\nexist.\n\n\n## WWW-Authenticate Headers\n\nThe controller's Edge APIs now include `WWW-Authenticate` headers on `401 Unauthorized` responses,\nper issuer: https://github.com/openziti/ziti/issues/3356. These headers provide insight into why\na token was rejected. The main benefit of these headers is to convey information for JWT backed\nAPI Sessions and API Session authentication where a token may not have been provided (missing),\nis used beyond its expiration date (expired), or the token has become invalid for any other\nreason (invalid).\n\n`www-authenticate` headers are provided as a single header instance with multiple challenge values\nseparate by commas.\n\n### No Credentials Provided\n\nWhen a request hits a protected endpoint without any credentials, a single `WWW-Authenticate` header\nis returned listing both accepted auth schemes as comma-separated challenges:\n\n```\nWWW-Authenticate: zt-session realm=\"zt-session\" error=\"missing\" error_description=\"no matching token was provided\",Bearer realm=\"openziti-oidc\" error=\"missing\" error_description=\"no matching token was provided\"\n```\n\n### Token Errors\n\nWhen a token is present but cannot be accepted, the header identifies the scheme and what went wrong:\n\n```\nWWW-Authenticate: Bearer realm=\"openziti-oidc\" error=\"expired\" error_description=\"token expired\"\nWWW-Authenticate: Bearer realm=\"openziti-oidc\" error=\"invalid\" error_description=\"token is invalid\"\nWWW-Authenticate: zt-session realm=\"zt-session\" error=\"invalid\" error_description=\"token is invalid\"\n```\n\n### OIDC External JWT — Primary Authentication\n\nWhen an auth policy requires an external JWT signer for primary authentication (e.g., a PKCE flow\nbacked by an ext-jwt signer), the header identifies which signers are accepted and what went wrong.\nMultiple accepted signers are pipe-delimited in the `id` and `issuer` parameters:\n\n```\nWWW-Authenticate: Bearer realm=\"openziti-primary-ext-jwt\" error=\"missing\" error_description=\"no matching token was provided\" id=\"signer-id-1|signer-id-2\" issuer=\"https://issuer1.example.com|https://issuer2.example.com\"\n```\n\nThe `error` value follows the same `missing`/`expired`/`invalid` pattern as standard bearer token errors.\n\n### OIDC External JWT — Secondary / MFA Authentication\n\nWhen an auth policy requires an external JWT signer as a secondary factor (step-up after primary\nauth succeeds), the header identifies the single required signer. The `error` value follows the\nsame `missing`/`expired`/`invalid` pattern:\n\n```\nWWW-Authenticate: Bearer realm=\"openziti-secondary-ext-jwt\" error=\"missing\" error_description=\"no matching token was provided\" id=\"<signer-id>\" issuer=\"<issuer>\"\n```\n\n### Anonymous Endpoints\n\nUnauthenticated endpoints such as version information do not return `WWW-Authenticate` headers.\n\n## HA Preferred Leaders\n\nControllers can be marked as a preferred leader. \n\n**Example Config**\n```yaml\ncluster:\n  dataDir: /home/{{ .Model.MustVariable \"credentials.ssh.username\" }}/fablab/ctrldata\n  preferredLeader: true\n```\n\nIf a controller that is not marked preferredLeader becomes a preferredLeader, it will check \nif there's a node available that is marked as preferred. If there is one, or one later\njoins the cluster, the non-preferred node will attempt to transfer leadership to the \nnode that is marked as preferred.\n\n## Expanded Dynamic Cost Range\n\nThe dynamic cost range for smart routing has been expanded beyond the previous 64K limit. Under high\nload, terminators could saturate the cost space, making dynamic cost values meaningless for routing\ndecisions and leading to uneven load distribution. The expanded range allows for more granular cost\ndifferentiation even under heavy load.\n\n## Circuit ID and Error in Dial Failures\n\nDial failures now return the circuit ID and, when available, the error that caused the circuit to fail.\nPreviously, the circuit ID was only returned on successful dials. Note that SDKs will need to be\nupdated to surface the circuit id when a dial failure happens.\n\n## Multi-Underlay Control Channels\n\nRouter-to-controller control channels now support multiple underlays with priority-based message routing.\nThis allows time-sensitive control messages (heartbeats, routing, circuit requests) to be separated from\noperational data (metrics, inspections) across dedicated TCP connections, preventing bulk operations from\ndelaying user-affecting control plane traffic. This feature does not yet allow specifying multiple \nnetwork interfaces to use, to load balance data across.\n\n## Dialing Identity Forwarded to Hosting SDK\n\nThe identity ID and name of the dialing client are now forwarded to the hosting SDK when a circuit is\nestablished. This allows hosting applications to identify which identity initiated the connection,\nenabling identity-aware request handling on the server side. This will require SDK updates to add this\nto the API for hosting applications.\n\n## Controller-Initiated Control Channel Dials (BETA)\n\nControllers can now dial routers to establish control channels. Previously, routers were solely\nresponsible for dialing controllers. This feature is designed for deployments where one or more\ncontrollers are in a private network that routers cannot reach, but the controllers can dial out.\nA common scenario is an HA cluster where some controllers are publicly reachable and some are in\na private network. External routers connect to the public controllers normally, and the private\ncontrollers dial out to the routers.\n\n### Router Configuration\n\nRouters can configure one or more control channel listeners. Each listener specifies a bind address,\nan advertise address (reported to the controller), and optional groups for matching.\n\n```yaml\nctrl:\n  listeners:\n    - bind: tls://0.0.0.0:6262\n      advertise: tls://router.example.com:6262\n      groups:\n        - default\n```\n\nThe router is the authoritative source of ctrl channel listener information, similar to link\nlisteners. When a router connects to any controller, it reports its configured `ctrlChanListeners`\nand the controller data model is updated automatically. This means that in most deployments —\nwhere the router can reach at least one controller — no manual configuration of listener addresses\nis needed on the controller side.\n\nThe `ctrlChanListeners` field can also be set via the CLI for cases where a router cannot reach\nany controller and thus cannot initialize the data model itself:\n\n```bash\nziti edge update edge-router myRouter --bootstrap-ctrl-chan-listener 'tls://router.example.com:6262=group1,group2'\n```\n\nGroups default to `[\"default\"]` if not specified.\n\n### Controller Configuration\n\nThe controller dialer is disabled by default and must be explicitly enabled. When enabled, the\ncontroller will dial routers that have control channel listeners configured and are not already\nconnected.\n\n```yaml\nctrl:\n  dialer:\n    enabled: true\n    groups:\n      - default\n    dialDelay: 30s\n    minRetryInterval: 1s\n    maxRetryInterval: 5m\n    retryBackoffFactor: 1.5\n    fastFailureWindow: 5s\n    queueSize: 32\n    maxWorkers: 10\n```\n\n- `enabled` - Enables the controller dialer (default: `false`)\n- `groups` - List of groups to match against router listener groups (default: `[\"default\"]`)\n- `dialDelay` - Delay before the controller starts dialing after boot (default: `30s`)\n- `minRetryInterval` - Minimum backoff delay between dial retries (default: `1s`)\n- `maxRetryInterval` - Maximum backoff delay between dial retries (default: `5m`)\n- `retryBackoffFactor` - Multiplier applied to the retry delay on each failure, jittered +/- 0.5 (default: `1.5`)\n- `fastFailureWindow` - If a connection drops within this window after connecting, backoff continues rather than resetting (default: `5s`)\n- `queueSize` - Maximum number of pending dial jobs in the worker pool queue (default: `32`)\n- `maxWorkers` - Maximum number of concurrent dial workers (default: `10`)\n\nThe controller will only dial routers whose listener groups overlap with the controller's configured\ngroups. When a router advertises multiple ctrl channel listener addresses, the dialer rotates through\nthem on each failure so that an unreachable address does not block attempts to the others.\n\n### Metrics\n\nThe controller dialer worker pool exposes the following metrics under the `ctrl_channel.dialer` prefix:\n\n- `ctrl_channel.dialer.queue_size` - Current number of pending dial jobs in the queue\n- `ctrl_channel.dialer.worker_count` - Current number of dial worker goroutines\n- `ctrl_channel.dialer.busy_workers` - Number of workers currently executing a dial\n- `ctrl_channel.dialer.work_timer` - Timer tracking the duration of each dial attempt\n\n## SDK Inspection Support\n\nNew CLI commands have been added for inspecting SDK state, useful for diagnosing terminator and\nconnectivity issues.\n\n**Note:** SDK inspection requires SDK support. Currently only the Go SDK supports inspection,\nas of version 1.5.0. Other SDKs will need to add support before these commands can be used\nwith them.\n\n### `ziti fabric inspect sdk`\n\nRetrieves SDK context inspection data from identities connected to routers.\n\n```\nziti fabric inspect sdk <target-selector> <identity-id>\n```\n\nThe `<target-selector>` is a regex matching router IDs (use `.*` for all routers). The\n`<identity-id>` is the identity whose SDK context you want to inspect. The command returns\ndetailed state from the connected SDK instance, including active services, terminators, and\nconnection status.\n\n### `ziti agent tunnel dump-sdk`\n\nDumps SDK context information from a running `ziti tunnel` process via the IPC agent.\n\n```\nziti agent tunnel dump-sdk\n```\n\nReturns JSON-formatted inspection data for all SDK contexts registered in the tunnel process,\nincluding service listeners, connections, and terminator state.\n\n## Current Beta Features\n\n* Basic Permission System\n* Alert Events\n* Controller-Initiated Control Channel Dials\n\n## Revocation System Improvements\n\nWhen a session is refreshed, the old refresh token's revocation is no longer created\nsynchronously through raft. Instead, revocations are queued in memory and flushed in\nbatches on a configurable interval. This removes the database and raft as a bottleneck\non token refreshes. If the old token is close to expiring, the revocation is skipped\nentirely.\n\nNew configuration tunables under `edge.oidc`:\n\n| Key | Default | Description |\n|-----|---------|-------------|\n| `revocationMinTokenLifetime` | unset | Skip revocation if the old token expires within this duration (must be < 50% of `refreshTokenDuration`) |\n| `revocationBucketInterval` | `1m` | Bucket window for batching revocations before flushing through raft |\n| `revocationBucketMaxSize` | `200` | Max revocations per raft entry |\n| `revocationMaxQueued` | `25000` | Max revocations queued in memory before dropping |\n| `revocationEnforcerFrequency` | `1m` | How often expired revocations are purged (leader only) |\n\n## Component Updates and Bug Fixes\n\n* github.com/openziti/agent: [v1.0.31 -> v1.0.33](https://github.com/openziti/agent/compare/v1.0.31...v1.0.33)\n* github.com/openziti/channel/v4: [v4.2.28 -> v4.3.9](https://github.com/openziti/channel/compare/v4.2.28...v4.3.9)\n    * [Issue #235](https://github.com/openziti/channel/issues/235) - Bump allowed hello message headers size to 16k from 4k\n    * [Issue #228](https://github.com/openziti/channel/issues/228) - Ensure that Underlay never return nil on MultiChannel\n    * [Issue #226](https://github.com/openziti/channel/issues/226) - Allow specifying a minimum number of underlays for a channel, regardless of underlay type\n    * [Issue #225](https://github.com/openziti/channel/issues/225) - Add ChannelCreated to the UnderlayHandler API to allow handlers to be initialized with the channel before binding\n    * [Issue #224](https://github.com/openziti/channel/issues/224) - Update the underlay dispatcher to allow unknown underlay types to fall through to the default\n    * [Issue #222](https://github.com/openziti/channel/issues/222) - Allow injecting the underlay type into messages\n\n* github.com/openziti/edge-api: [v0.26.47 -> v0.27.5](https://github.com/openziti/edge-api/compare/v0.26.47...v0.27.5)\n    * [Issue #175](https://github.com/openziti/edge-api/issues/175) - ctrlChanListeners should have x-omit-empty: false attribute\n    * [Issue #170](https://github.com/openziti/edge-api/issues/170) - Add preferredLeader flag to controllers\n    * [Issue #167](https://github.com/openziti/edge-api/issues/167) - Add ctrlChanListeners to router types\n    * [Issue #164](https://github.com/openziti/edge-api/issues/164) - Add permissions list to identity\n\n* github.com/openziti/foundation/v2: [v2.0.72 -> v2.0.90](https://github.com/openziti/foundation/compare/v2.0.72...v2.0.90)\n    * [Issue #472](https://github.com/openziti/foundation/issues/472) - Add support for multi-bit set/get to AtomicBitSet\n    * [Issue #464](https://github.com/openziti/foundation/issues/464) - Add support for -pre in versions\n    * [Issue #455](https://github.com/openziti/foundation/issues/455) - Correctly close goroutine pool when external close is signaled\n    * [Issue #452](https://github.com/openziti/foundation/issues/452) - Goroutine pool with a min worker count of 1 can drop to 0 workers due to race condition\n\n* github.com/openziti/identity: [v1.0.111 -> v1.0.128](https://github.com/openziti/identity/compare/v1.0.111...v1.0.128)\n    * [Issue #68](https://github.com/openziti/identity/issues/68) - Shutdown file watcher when stopping identity watcher\n\n* github.com/openziti/metrics: [v1.4.2 -> v1.4.5](https://github.com/openziti/metrics/compare/v1.4.2...v1.4.5)\n    * [Issue #58](https://github.com/openziti/metrics/issues/58) - Add GaugeFloat64 support\n    * [Issue #56](https://github.com/openziti/metrics/issues/56) - underlying resources of reference counted meters are not cleaned up when reference count hits zero\n\n* github.com/openziti/runzmd: [v1.0.80 -> v1.0.90](https://github.com/openziti/runzmd/compare/v1.0.80...v1.0.90)\n* github.com/openziti/sdk-golang: [v1.2.3 -> v1.6.0](https://github.com/openziti/sdk-golang/compare/v1.2.3...v1.6.0)\n    * [Issue #895](https://github.com/openziti/sdk-golang/issues/895) - Limit effect sudden rtt spikes can have on rtt moving average\n    * [Issue #902](https://github.com/openziti/sdk-golang/issues/902) - Inspect response message content types are mixed up\n    * [Issue #887](https://github.com/openziti/sdk-golang/issues/887) - Fix listener manager cleanup\n    * [Issue #886](https://github.com/openziti/sdk-golang/issues/886) - When controller is busy during service refresh, backoff and retry instead of falling back to full refresh\n    * [Issue #885](https://github.com/openziti/sdk-golang/issues/885) - Only compare relevant service fields when looking for changes\n    * [Issue #884](https://github.com/openziti/sdk-golang/issues/884) - Add deadline for bind establishment\n    * [Issue #883](https://github.com/openziti/sdk-golang/issues/883) - Router level listener can be left open if multi-listener closes during listener establishment\n    * [Issue #877](https://github.com/openziti/sdk-golang/issues/877) - Handle differences in xgress eof/end-of-circuit handling by adding a capabilities exchange\n    * [Issue #832](https://github.com/openziti/sdk-golang/issues/832) - Fuzz session refresh timers\n    * [Issue #879](https://github.com/openziti/sdk-golang/issues/879) - Return the connId in inspect response\n    * [Issue #878](https://github.com/openziti/sdk-golang/issues/878) - Fix responses from rx goroutines\n    * [Issue #874](https://github.com/openziti/sdk-golang/issues/874) - Add inspect support at the context level\n    * [Issue #871](https://github.com/openziti/sdk-golang/issues/871) - Make SDK better at sticking to MaxTerminator terminators\n    * [Issue #708](https://github.com/openziti/sdk-golang/issues/708) - Support for Go's built-in context in Dial methods\n    * [Issue #860](https://github.com/openziti/sdk-golang/issues/860) - Make the dialing identity's id and name available on dialed connections\n    * [Issue #857](https://github.com/openziti/sdk-golang/issues/857) - Use new error code and retry hints to correctly react to terminator errors\n    * [Issue #847](https://github.com/openziti/sdk-golang/issues/847) - Ensure the initial version check succeeds, to ensure we don't legacy sessions on ha or oidc-enabled controllers\n    * [Issue #824](https://github.com/openziti/sdk-golang/pull/824) - release notes and hard errors on no TOTP handler breaks partial auth events\n    * [Issue #818](https://github.com/openziti/sdk-golang/issues/818) - Full re-auth should not clear services list, as that breaks the on-change logic\n    * [Issue #817](https://github.com/openziti/sdk-golang/issues/817) - goroutines can get stuck when iterating over randomized HA controller list\n    * [Issue #736](https://github.com/openziti/sdk-golang/issues/736) - Migrate from github.com/mailru/easyjson\n    * [Issue #813](https://github.com/openziti/sdk-golang/issues/813) - SDK doesn't stop close listener when it detects that a service being hosted gets deleted\n    * [Issue #811](https://github.com/openziti/sdk-golang/issues/811) - Credentials are lost when explicitly set\n    * [Issue #807](https://github.com/openziti/sdk-golang/issues/807) - Don't send close from rxer to avoid blocking\n    * [Issue #800](https://github.com/openziti/sdk-golang/issues/800) - Tidy create service session logging\n\n* github.com/openziti/secretstream: [v0.1.39 -> v0.1.49](https://github.com/openziti/secretstream/compare/v0.1.39...v0.1.49)\n* github.com/openziti/storage: [v0.4.26 -> v0.4.39](https://github.com/openziti/storage/compare/v0.4.26...v0.4.39)\n    * [Issue #122](https://github.com/openziti/storage/issues/122) - StringFuncNode has incorrect nil check, allowing panic\n    * [Issue #120](https://github.com/openziti/storage/issues/120) - Change post tx commit constraint handling order\n    * [Issue #119](https://github.com/openziti/storage/issues/119) - Add ContextDecorator API\n\n* github.com/openziti/transport/v2: [v2.0.188 -> v2.0.215](https://github.com/openziti/transport/compare/v2.0.188...v2.0.215)\n    * [Issue #31](https://github.com/openziti/transport/issues/31) - ipv6 Transport Address Parsing\n    * [Issue #149](https://github.com/openziti/transport/issues/149) - Archive transwarp code\n\n* github.com/openziti/xweb/v3: [v2.3.4 -> v3.0.4](https://github.com/openziti/xweb/compare/v2.3.4...v3.0.4)\n    * [Issue #32](https://github.com/openziti/xweb/issues/32) - watched identities sometimes don't reload when changed\n\n* github.com/openziti/go-term-markdown: v1.0.1 (new)\n* github.com/openziti/ziti/v2: [v1.6.8 -> v2.0.0](https://github.com/openziti/ziti/compare/v1.6.8...v2.0.0)\n    * [Issue #3721](https://github.com/openziti/ziti/issues/3721) - Add CreateCircuitV3 to controller\n    * [Issue #3719](https://github.com/openziti/ziti/issues/3719) - Allow binding specific inspects to pass through to xgress listener implementations\n    * [Issue #3696](https://github.com/openziti/ziti/issues/3696) - oidc provider is non-deterministic for wildcard certs\n    * [Issue #3681](https://github.com/openziti/ziti/issues/3681) - coalesce OIDC JWT revocations to reduce controller write pressure\n    * [Issue #3683](https://github.com/openziti/ziti/issues/3683) - add fablab test for testing flow control changes over a longer term\n    * [Issue #3673](https://github.com/openziti/ziti/issues/3673) - revocation build-up in db and rdm\n    * [Issue #3674](https://github.com/openziti/ziti/issues/3674) - Update to Go 1.26\n    * [Issue #3496](https://github.com/openziti/ziti/issues/3496) - MFA TOTP Enrollment During OIDC Authentication Does Not Work\n    * [Issue #3609](https://github.com/openziti/ziti/issues/3609) - Stabilize terminator creation test for 2.0\n    * [Issue #3648](https://github.com/openziti/ziti/issues/3648) - tunnel: myCopy logs router ID as circuitId, causing misleading debug output\n    * [Issue #3658](https://github.com/openziti/ziti/issues/3658) - Raft cluster join fails with \"hello message too big\" when using long hostnames\n    * [Issue #3626](https://github.com/openziti/ziti/issues/3626) - controller: overlay bind point produces malformed URL in /versions apiBaseUrls\n    * [Issue #3635](https://github.com/openziti/ziti/issues/3635) - Allow controllers to dial routers to support more topologies\n    * [Issue #3607](https://github.com/openziti/ziti/issues/3607) - linux installer not upgradable from v1\n    * [Issue #3650](https://github.com/openziti/ziti/issues/3650) - Reroute doesn't proactively clean up orphaned route entries\n    * [Issue #3571](https://github.com/openziti/ziti/issues/3571) - Ensure 2.0 backwards compatibility with 1.6 and 1.5 using the smoketest\n    * [Issue #3636](https://github.com/openziti/ziti/issues/3636) - Adaptive rate limiter should use success rate rather than queue position\n    * [Issue #3600](https://github.com/openziti/ziti/issues/3600) - Add a preferredLeader flag, allow selected nodes to be preferred for raft leader, if they're available\n    * [Issue #3642](https://github.com/openziti/ziti/issues/3642) - Use xgress_common.Connection type for xgress_transport and xgress_proxy\n    * [Issue #3597](https://github.com/openziti/ziti/issues/3597) - Enable OIDC API by default\n    * [Issue #3624](https://github.com/openziti/ziti/issues/3624) - Multi-underlay control channel doesn't correctly handle lack of group secret on non-grouped underlays\n    * [Issue #3613](https://github.com/openziti/ziti/issues/3613) - Initializing cluster from existing db using `db:` config settings results in panic\n    * [Issue #3620](https://github.com/openziti/ziti/issues/3620) - Controller control channel heartbeats config parsing was erroneously nested under options parsing\n    * [Issue #3619](https://github.com/openziti/ziti/issues/3619) - Router connect events full sync interval was using min/max values meant for the batch interval\n    * [Issue #3618](https://github.com/openziti/ziti/issues/3618) - Router interfaceDiscovery.minReportInterval value was being set to checkInterval\n    * [Issue #3617](https://github.com/openziti/ziti/issues/3617) - Transit router disabled flag not passed through raft command structure\n    * [Issue #3333](https://github.com/openziti/ziti/issues/3333) - Updb user-lockout triggered by successful login attempts\n    * [Issue #3599](https://github.com/openziti/ziti/issues/3599) - Add gap detection and handling to router data model\n    * [Issue #3356](https://github.com/openziti/ziti/issues/3356) - Add WWW-Authenticate Headers\n    * [Issue #3074](https://github.com/openziti/ziti/issues/3074) - Dynamic cost range is too limited\n    * [Issue #3558](https://github.com/openziti/ziti/issues/3558) - terminator cost increased on egress dial success, not on circuit completion\n    * [Issue #3556](https://github.com/openziti/ziti/issues/3556) - global circuit costs not cleared when terminator is deleted\n    * [Issue #3557](https://github.com/openziti/ziti/issues/3557) - costing calculation for the weighted terminator selection strategy  is incorrect\n    * [Issue #2512](https://github.com/openziti/ziti/issues/2512) - Return circuit ID and error in dial failures\n    * [Issue #3569](https://github.com/openziti/ziti/issues/3569) - Version 2.0+ routers should not connect to controllers which do not support JWT formatted legacy sessions\n    * [Issue #3565](https://github.com/openziti/ziti/issues/3565) - Link dialer save 'is first conn' true, so all dials claim to be first, causing potential race condition\n    * [Issue #3550](https://github.com/openziti/ziti/issues/3550) - Support multi-underlay control channels\n    * [Issue #3535](https://github.com/openziti/ziti/issues/3535) - Remove the legacy xgress_edge_tunnel implementation\n    * [Issue #3547](https://github.com/openziti/ziti/issues/3547) - Add support for sending the dialing identity id and name to the hosting sdk\n    * [Issue #3541](https://github.com/openziti/ziti/issues/3541) - Remove option to disable the router data model in the controller\n    * [Issue #3540](https://github.com/openziti/ziti/issues/3540) - Handle UDP difference between proxy and tproxy implementations\n    * [Issue #3527](https://github.com/openziti/ziti/issues/3527) - ER/T UDP tunnels keep closed connections for 30s, preventing potential new good connections in that time\n    * [Issue #3526](https://github.com/openziti/ziti/issues/3526) - ER/T half-close logic is incorrect\n    * [Issue #3524](https://github.com/openziti/ziti/issues/3524) - Provide more error context to SDKs for terminator errors\n    * [Issue #3509](https://github.com/openziti/ziti/issues/3509) - Enforce policy on the router for oidc sessions, by closing open circuits and terminators when service access is lost\n    * [Issue #3531](https://github.com/openziti/ziti/issues/3531) - Remove created/updated/deleted terminator events. Obsoleted by entity change events.\n    * [Issue #3532](https://github.com/openziti/ziti/issues/3532) - Removed deprecated create identity <type> subcommands\n    * [Issue #3521](https://github.com/openziti/ziti/issues/3521) - Cleanup CLI to remove calls to os.Exit to be embed friendlier\n    * [Issue #3516](https://github.com/openziti/ziti/issues/3516) - Remove support for create terminator v1\n    * [Issue #3512](https://github.com/openziti/ziti/issues/3512) - Remove legacy link management code from the controller\n    * [Issue #3511](https://github.com/openziti/ziti/issues/3511) - router proxy mode fails to resolve interface if binding is 0.0.0.0\n    * [Issue #3503](https://github.com/openziti/ziti/issues/3503) - Allow routers to request current cluster membership information\n    * [Issue #3501](https://github.com/openziti/ziti/issues/3501) - Get cluster membership information from raft directly, rather than trying to cache it in the DB\n    * [Issue #3500](https://github.com/openziti/ziti/issues/3500) - Set a router data model timeline when initializing a new HA setup, rather than letting it stay blank\n    * [Issue #3504](https://github.com/openziti/ziti/issues/3504) - Reduce router data model full state updates\n    * [Issue #3492](https://github.com/openziti/ziti/pull/3492) - Bump openziti/ziti-console-assets from 3.12.9 to 4.0.0 in /dist/docker-images/ziti-controller in the all group\n    * [Issue #3484](https://github.com/openziti/ziti/issues/3484) - router ctrl channel handler for handling cluster changes has an initialization race condition\n    * [Issue #3477](https://github.com/openziti/ziti/issues/3477) - Optionally enable model changes triggered by login to be non-blocking and to be droppable if the system is under load\n    * [Issue #3473](https://github.com/openziti/ziti/issues/3473) - Enable tls handshake rate limiter by default and tweak default values.\n    * [Issue #3471](https://github.com/openziti/ziti/issues/3471) - Go tunneler is ignoring host config MaxConnections\n    * [Issue #3469](https://github.com/openziti/ziti/issues/3469) - Only send model updates on resubscribe if the RDM index has advanced\n    * [Issue #2573](https://github.com/openziti/ziti/issues/2573) - An edge router in a tight restart loop causes a resource leak on routers to which it connects.\n    * [Issue #3430](https://github.com/openziti/ziti/issues/3430) - Add permissions list to identity\n    * [Issue #2109](https://github.com/openziti/ziti/issues/2109) - Add Edge Management Read Only Capability\n    * [Issue #3435](https://github.com/openziti/ziti/issues/3435) - Add edge management API permissions by entity type and action\n    * [Issue #3441](https://github.com/openziti/ziti/issues/3441) - Update router connection tracker to interrogate active connections\n    * [Issue #3451](https://github.com/openziti/ziti/issues/3451) - ci - compare only stable releases when promoting\n    * [Issue #3437](https://github.com/openziti/ziti/issues/3437) - SDK OIDC token updates to routers should return an error if invalid\n    * [Issue #3348](https://github.com/openziti/ziti/issues/3348) - Unable to clear/reset the \"tags\" property on an entity to an empty object\n    * [Issue #3452](https://github.com/openziti/ziti/issues/3452) - `ziti agent cluster add` has bad behavior if the add address doesn't match the advertise address\n    * [Issue #3410](https://github.com/openziti/ziti/issues/3410) - Consolidate fabric REST API code with edge management and edge client code\n    * [Issue #3425](https://github.com/openziti/ziti/issues/3425) - RDM not properly responding to tunneler enabled flag changes\n    * [Issue #3420](https://github.com/openziti/ziti/issues/3420) - The terminator id cache uses the same id for all terminators in a host.v2 config, resulting in a single terminator\n    * [Issue #3419](https://github.com/openziti/ziti/issues/3419) - When using the router data model, precedence specified on the per-service identity mapping are incorrectly interpreted\n    * [Issue #3318](https://github.com/openziti/ziti/issues/3318) - Terminator creation seems to slow exponentially as the number of terminators rises from 10k to 20k to 40k\n    * [Issue #3407](https://github.com/openziti/ziti/issues/3407) - The CLI doesn't properly pass JWT authentication information to websocket endpoints\n    * [Issue #3359](https://github.com/openziti/ziti/issues/3359) - Ensure router data model subscriptions have reasonable performance and will scale\n    * [Issue #3354](https://github.com/openziti/ziti/issues/3354) - SDK/ENV Info from SDKs is not distributed in HA\n    * [Issue #3381](https://github.com/openziti/ziti/issues/3381) - the fabric service REST apis are missing the maxIdleTime property\n    * [Issue #3382](https://github.com/openziti/ziti/issues/3382) - Legacy service sessions generated pre-1.7.x are incompatible with v1.7.+ and need to be cleared\n    * [Issue #3339](https://github.com/openziti/ziti/issues/3339) - get router ctrl.endpoint from ctrls claim in JWT\n    * [Issue #3378](https://github.com/openziti/ziti/issues/3378) - login with file stopped working\n    * [Issue #3349](https://github.com/openziti/ziti/issues/3349) - UPDB OIDC login returns wrong content type\n    * [Issue #2324](https://github.com/openziti/ziti/issues/2324) - Add Ext-JWT/OIDC enrollment\n    * [Issue #3346](https://github.com/openziti/ziti/issues/3346) - Fix confusing attempt logging\n    * [Issue #3337](https://github.com/openziti/ziti/issues/3337) - Router reports \"no xgress edge forwarder for circuit\"\n    * [Issue #3345](https://github.com/openziti/ziti/issues/3345) - Clean up connect events tests and remove global XG registry\n    * [Issue #3264](https://github.com/openziti/ziti/issues/3264) - Allow routers to generate alert events in cases of service misconfiguration\n    * [Issue #3321](https://github.com/openziti/ziti/issues/3321) - Health Check API missing base path on discovery endpoint\n    * [Issue #3323](https://github.com/openziti/ziti/issues/3323) - router/tunnel static services fail to bind unless new param protocol is defined\n    * [Issue #3309](https://github.com/openziti/ziti/issues/3309) - Detect link connections meant for another router\n    * [Issue #3286](https://github.com/openziti/ziti/issues/3286) - edge-api binding doesn't have the correct path on discovery endpoints\n    * [Issue #3297](https://github.com/openziti/ziti/issues/3297) - stop promoting hotfixes downstream\n    * [Issue #3295](https://github.com/openziti/ziti/issues/3295) - make ziti tunnel service:port pairs optional\n    * [Issue #3291](https://github.com/openziti/ziti/issues/3291) - replace decommissioned bitnami/kubectl\n    * [Issue #3277](https://github.com/openziti/ziti/issues/3277) - Router can deadlock on closing a connection if the incoming data channel is full\n    * [Issue #3269](https://github.com/openziti/ziti/issues/3269) - Add host-interfaces config type\n    * [Issue #3258](https://github.com/openziti/ziti/issues/3258) - Add config type proxy.v1 so proxies can be defined dynamically for the ER/T\n    * [Issue #3259](https://github.com/openziti/ziti/issues/3259) - Interfaces config type not added due to wrong name\n    * [Issue #3265](https://github.com/openziti/ziti/issues/3265) - Forwarding errors should log at debug, since they are usual part of circuit teardown\n    * [Issue #3261](https://github.com/openziti/ziti/issues/3261) - ER/T dialed xgress connections may only half-close when peer is fully closed\n\n","mentions_count":1},{"url":"https://api.github.com/repos/openziti/ziti/releases/303383747","assets_url":"https://api.github.com/repos/openziti/ziti/releases/303383747/assets","upload_url":"https://uploads.github.com/repos/openziti/ziti/releases/303383747/assets{?name,label}","html_url":"https://github.com/openziti/ziti/releases/tag/v2.0.0-pre9","id":303383747,"author":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"node_id":"RE_kwDODVFMN84SFUTD","tag_name":"v2.0.0-pre9","target_commitish":"main","name":"v2.0.0-pre9","draft":false,"immutable":false,"prerelease":true,"created_at":"2026-03-30T19:50:45Z","updated_at":"2026-03-30T19:56:59Z","published_at":"2026-03-30T19:56:59Z","assets":[{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/385003196","id":385003196,"node_id":"RA_kwDODVFMN84W8q68","name":"checksums.sha256.txt","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"text/plain; charset=utf-8","state":"uploaded","size":790,"digest":"sha256:f78041c35bf3745aaf4524d026a5e737255514acc7e1dd50446da627ff15a230","download_count":6,"created_at":"2026-03-30T19:56:55Z","updated_at":"2026-03-30T19:56:56Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre9/checksums.sha256.txt"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/385003193","id":385003193,"node_id":"RA_kwDODVFMN84W8q65","name":"sbom-v2.0.0-pre9.spdx.json","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/json","state":"uploaded","size":1008832,"digest":"sha256:23537d7fccaa0f7e4443f59e016f61655609e392704de7c8cf00a1ea2f3cbedd","download_count":3,"created_at":"2026-03-30T19:56:55Z","updated_at":"2026-03-30T19:56:56Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre9/sbom-v2.0.0-pre9.spdx.json"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/385003194","id":385003194,"node_id":"RA_kwDODVFMN84W8q66","name":"source-v2.0.0-pre9.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":3787651,"digest":"sha256:c226e36e7a370069be88d18a01a385b5c448ce063175b3d7f6f13092bbcd4520","download_count":5,"created_at":"2026-03-30T19:56:55Z","updated_at":"2026-03-30T19:56:56Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre9/source-v2.0.0-pre9.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/385003178","id":385003178,"node_id":"RA_kwDODVFMN84W8q6q","name":"ziti-darwin-amd64-2.0.0-pre9.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":53964149,"digest":"sha256:86149ff58a3a9ec544c6de8b6c51433982505b68aea0ce111a0a6c91c62d9600","download_count":6,"created_at":"2026-03-30T19:56:52Z","updated_at":"2026-03-30T19:56:55Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre9/ziti-darwin-amd64-2.0.0-pre9.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/385003180","id":385003180,"node_id":"RA_kwDODVFMN84W8q6s","name":"ziti-darwin-arm64-2.0.0-pre9.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":50326343,"digest":"sha256:54c4f97658ebf92f1afb3461896014dd6f8fbcede707ca9968a04ff53a1917a4","download_count":5,"created_at":"2026-03-30T19:56:52Z","updated_at":"2026-03-30T19:56:55Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre9/ziti-darwin-arm64-2.0.0-pre9.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/385003176","id":385003176,"node_id":"RA_kwDODVFMN84W8q6o","name":"ziti-linux-amd64-2.0.0-pre9.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":55266672,"digest":"sha256:99a7661db111c8ccbed29601b8e9d9e05364928af4b24ba94ccf66e6234adb63","download_count":12,"created_at":"2026-03-30T19:56:52Z","updated_at":"2026-03-30T19:56:55Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre9/ziti-linux-amd64-2.0.0-pre9.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/385003177","id":385003177,"node_id":"RA_kwDODVFMN84W8q6p","name":"ziti-linux-arm-2.0.0-pre9.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":51775242,"digest":"sha256:cdd8147be1db049a159f2545a122deb80869dc4c7cfabff6d832361a5722c587","download_count":6,"created_at":"2026-03-30T19:56:52Z","updated_at":"2026-03-30T19:56:55Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre9/ziti-linux-arm-2.0.0-pre9.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/385003191","id":385003191,"node_id":"RA_kwDODVFMN84W8q63","name":"ziti-linux-arm64-2.0.0-pre9.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":51780039,"digest":"sha256:37a0f560be11d5066ce1b9c5483436e75d19fdaeb427712a61d9c1ea6db7ceee","download_count":7,"created_at":"2026-03-30T19:56:55Z","updated_at":"2026-03-30T19:56:58Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre9/ziti-linux-arm64-2.0.0-pre9.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/385003179","id":385003179,"node_id":"RA_kwDODVFMN84W8q6r","name":"ziti-windows-amd64-2.0.0-pre9.zip","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/zip","state":"uploaded","size":44651390,"digest":"sha256:86f61eb9e60287f4ab8c383b186bf71504d64a50bea5b99ce897fcc5c232d992","download_count":8,"created_at":"2026-03-30T19:56:52Z","updated_at":"2026-03-30T19:56:55Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre9/ziti-windows-amd64-2.0.0-pre9.zip"}],"tarball_url":"https://api.github.com/repos/openziti/ziti/tarball/v2.0.0-pre9","zipball_url":"https://api.github.com/repos/openziti/ziti/zipball/v2.0.0-pre9","body":"# Release 2.0.0\n\n## What's New\n\nThis is the next major version release of OpenZiti, following the 1.0 release in April 2024.\nOf particular note is that HA controllers are now considered ready for general use.\nThis release also introduces a new permissions model, OIDC/JWT token-based enrollment, \nclustering performance improvements, and a number of other features and fixes. Because \nsome of these changes are not backwards compatible with older routers, we're marking this \nas a major version bump.\n\n### HA Controllers are now considered ready for general use\n\nThis is a pretty big milestone and marks the completion of work that's been ongoing for a couple of years.\nThe HA work has brought with it some notable changes. Authentication now uses JWTs by default. Using JWTs\nmeans the controllers don't need to store session and propagate them to the routers. This removes a bottleneck\nfrom the network and allows the load to be more easily distributed among controllers and routers.\n\nTo support distributed authentication, the routers now get a bespoke version of the data model. In addition\nto enabling distributed authentication this will allow us to remove the need for service polling and further \nreduce the load on controllers in the future.\n\n### Router Compatibility\n\nRelated to the JWT work, routers with version 2.+ will only work with controllers that are version 2.+. This means\nto upgrade your network, controllers should be upgraded first. Routers can then be upgraded individually.\n\n2.x routers should still work fine with older router versions.\n\nWe try very hard to avoid breaking changes like this, but sometimes the engineering trade-offs lead there. This change\nwas first made in the 1.7 release. That release has not been marked stable, and we have no plans to do so, because\nof the backwards incompatibility. \n\nIf you need to support in the 1.6.12+ range, see the section \"OIDC enabled by default\".\n\n### New Permissions Model (BETA)\n\nAs one feature goes out of beta, another arrives into beta. This release introduces a new permissions system\nfor more fine grained control to the management API. It's not expected to change, but may do so based on feedback\nfrom users.\n\n### Updated Release Process\n\nWe have moved to creating `-preN` releases for major and minor versions. This way we can put out release candidates,\nor feature previews and put them through internal testing and let interested folks from the community try them out.\nThen, when we're ready, we can run the full validation suite against the last pre-release and retag it. \n\nPatch releases won't have `-preN` and should contain only high priority bug fixes.\n\n### Deprecation Cleanup\n\nSince we already have a breaking change, we're removing some other backwards compatibility code.\n\n* Controller managed links \n    * Router managed links were introduced in v0.30.0. \n    * If you're upgrading from an older versions, you'll want to upgrade to the latest 1.x release before jumping to 2.x\n    * Github tracking issue: https://github.com/openziti/ziti/issues/3512\n* `ziti edge create identity <type>`\n    * Identity types other than router were removed in v0.30.2\n    * The `type` can be dropped from the CLI command\n    * Github tracking issue: https://github.com/openziti/ziti/issues/3532\n* Terminator create/update/delete events\n    * These have been superseded by entity change events, which also have create/update/delete events for terminators\n    * Entity change events were introduced in v0.28.0\n    * Github tracking issue: https://github.com/openziti/ziti/issues/3531\n* `xgress_edge_tunnel` v1\n    * This is the first implementation of the tunneler in edge-router code (ER/T) which used legacy api sessions and services\n    * The v2 version uses the router data model and was introduced in v0.30.x\n    * Github tracking issue: https://github.com/openziti/ziti/issues/3516\n\n### Legacy Session Deprecation\n\nOIDC sessions are now preferred. They are the default, or will become the default for SDKs and tunnelers. They are also required\nwhen running HA. Legacy API and service session are now deprecated and will be removed in the OpenZiti v3.0.0 release. \n\n### Additional Features\n\n* Controllers can now optionally bind APIs using an OpenZiti identity\n* `ziti edge login` now supports the `--network-identity` flag to authenticate and establish connections through the Ziti overlay network\n* `ziti edge login` now supports using a bearer token with `--token` for authentication. The token is expected to be\n  provided as just the JWT, not with the \"Bearer \" prefix\n* Identity configuration can now be loaded from files or environment variables for flexible deployment scenarios\n* Identities can now be provisioned just-in-time through OIDC/JWT token-based enrollment\n* Multiple model updates can now be in-flight at the same time, improving clustering performance\n* Authentication-related model updates can now be non-blocking and even dropped if the system is too busy\n* Routers now provide more error context to SDKs for terminator errors, enabling better retry behavior\n* New `proxy.v1` config type for dynamic service proxies (originally released in 1.7.0)\n* New alert event type for surfacing operational issues to network operators - Beta (originally released in 1.7.0)\n* New Azure Service Bus event sink for streaming controller events, contributed by @ffaraone (originally released in 1.7.0)\n* Bundled ZAC upgraded to 4.0\n* Build updated to Go 1.25\n* CLI cleaned up to remove calls to `os.Exit`, making it more friendly for embedding\n* OIDC is now enabled by default\n* Controller Edge APIs now return `WWW-Authenticate` response headers on `401 Unauthorized` responses, giving clients actionable information about which auth methods are accepted and what went wrong\n* HA Controllers can be marked as 'preferredLeader' via config\n* Dynamic cost range for smart routing expanded beyond the previous 64K limit\n* Dial failures now return the circuit ID and error information for easier debugging\n* Router-to-controller control channels now support multiple underlays with priority-based message routing\n* The dialing identity's ID and name are now forwarded to the hosting SDK\n* Controllers can now dial routers to establish control channels, enabling connectivity when routers are behind firewalls (Beta)\n* Refresh-token revocations are now batched and best-effort, removing the database/raft bottleneck on token refreshes\n* `ziti edge quickstart` now always runs in HA mode. The `ha` subcommand has been removed. Use\n  `ziti edge quickstart join` to add additional members to the cluster. Note: existing quickstart instances\n  are not compatible with the new HA-only mode and will need to be recreated.\n* The `--clustered` flag on `ziti create config controller` has been removed; the generated config is always\n  cluster-ready. If you have scripts passing `--clustered`, remove it.\n\n## Basic Permission System (BETA)\n\nAdded a basic permission system that allows more control over identity access to controller management API operations. \nThis replaces the previous binary admin/non-admin model with a more flexible permission system.\n\n**NOTE:** This feature is in BETA, primarily so we can get feedback on which permissions make sense. The implementation is unlikely to change\nbut the set of exposed permissions may grow, shrink or change based on user feedback. \n\n### Permission Model\n\nThe permission system supports three levels of authorization:\n\n  1. **Global Permissions**: System-wide access levels\n     - `admin` - Full access to all operations. This is still controlled by the `isAdmin` flag on identity\n     - `admin_readonly` - Read-only access to all resources except debugging facilities inspect and validate\n\n  2. **Entity-Level Permissions**: Full CRUD access to specific entity types\n     - Granting an entity-level permission (e.g., `service`) provides complete create, read, update, and delete access for that entity type\n\n  3. **Action-Level Permissions**: Specific operation access on entity types\n     - Fine-grained control using the pattern `<entity>.<action>` (e.g., `service.read`, `identity.update`)\n     - Supports `create`, `read`, `update`, and `delete` actions per entity type\n\n### Supported Entity Permissions\n\nThe following entity-level permissions are available:\n\n- `auth-policy` - Authentication policy management\n- `ca` - Certificate Authority management\n- `config` - Configuration management\n- `config-type` - Configuration type management\n- `edge-router-policy` - Edge router policy management\n- `enrollment` - Enrollment management\n- `external-jwt-signer` - External JWT signer management\n- `identity` - Identity management\n- `posture-check` - Posture check management\n- `router` - Edge and transit router management\n- `service` - Service management\n- `service-policy` - Service policy management\n- `service-edge-router-policy` - Service edge router policy management\n- `terminator` - Terminator management\n- `ops` - Operational resources (API sessions, sessions, circuits, links, inspect and validate)\n\n### Permission Assignment\n\nPermissions are assigned to identities via the `permissions` field in the identity resource. Multiple permissions can be granted to a single identity, and permissions are additive.\n\n### Cross-Entity Operations\n\nListing related entities through an entity's endpoints requires appropriate permissions for the related entity type. For example:\n- Listing services for a service-policy requires `service.read` permission\n- Listing identities for an edge-router-policy requires `identity.read` permission\n- Listing configs for a service requires `config.read` permission\n\n**NOTE:** \nMore permissions than expected may be required when performing actions through the CLI or ZAC. Take for example, when an identity \nhas `config.create` and is attempting to create a new config. The CLI may fail if the identity doesn't have `config-type.read`\nas well because it will need to look up the config type id that corresponds to the given config type name.\n\nSimilar cross entity read permissions may be required when creating services.\n\n### Admin Protection\n\nNon-admin identities cannot:\n- Create identities with the `isAdmin` flag\n- Create identities with any permissions granted\n- Modify admin-related fields on existing identities\n- Update or delete admin identities\n- Grant permissions to identities\n\nThese protections ensure that privilege escalation is prevented and admin access remains controlled.\n\n\n## Binding Controller APIs With Identity\n\nController APIs can now be bound to an OpenZiti overlay network identity, allowing secure communication through\nthe Ziti network. This is useful for scenarios where you want to expose controller APIs only through the overlay\nnetwork rather than on a standard network interface.\n\n### Configuration Structure\n\nA standard `bindPoint` configuration looks like this:\n```text\n    bindPoints:\n      - interface: 127.0.0.1:18441\n        address: 127.0.0.1:18441\n```\n\nTo bind controller APIs to an OpenZiti identity, add an additional `identity` block to your `bindPoints`. The\nidentity configuration specifies where to load the Ziti identity file and which service to bind it to:\n\n```text\n    bindPoints:\n      - interface: 127.0.0.1:18441\n        address: 127.0.0.1:18441\n      - identity:\n          file: \"c:/temp/ctrl.testing/ctrl.identity.json\"\n          service: \"mgmt\"\n```\n\n### Supported Configuration Options\n\n- `file`: Path to a Ziti identity JSON file containing the controller's identity and enrollment certificate\n- `env`: Name of an environment variable containing a base64-encoded Ziti identity (alternative to `file`)\n- `service`: The name of the Ziti service to bind the controller API to\n\n### Using Environment Variables\n\nFor deployments where storing identity files on disk is not preferred, you can reference a base64-encoded\nidentity file from an environment variable. The environment variable should contain the base64-encoded contents\nof the identity JSON file.\n\nFor example, if an environment variable named `ZITI_CTRL_IDENTITY` contains a base64-encoded identity file:\n\n```text\n    bindPoints:\n      - interface: 127.0.0.1:18441\n        address: 127.0.0.1:18441\n      - identity:\n          env: ZITI_CTRL_IDENTITY\n          service: \"mgmt\"\n```\n\n### IPv6 Support\n\nBoth IPv4 and IPv6 addresses are supported for standard bind points. IPv6 addresses should be specified in bracket\nnotation with a port number:\n\n```text\n    bindPoints:\n      - interface: \"[::1]:18441\"\n        address: \"[::1]:18441\"\n      - identity:\n          file: \"/path/to/identity.json\"\n          service: \"mgmt\"\n```\n\n## CLI Enhancements for Identity-Based Connections\n\nThe `ziti edge login` command and REST client utilities have been enhanced to support identity-based connections\nthrough the Ziti overlay network.\n\n### New `--network-identity` Flag for `ziti edge login`\n\nThe `ziti edge login` command now includes a `--network-identity` flag that allows you to authenticate to a Ziti\ncontroller through the overlay network using a Ziti identity:\n\n```bash\nziti edge login https://ziti.mgmt.apis.local:1280 \\\n  --username myuser \\\n  --password mypass \\\n  --network-identity /path/to/identity.json\n```\n\nThis is useful when the controller is only accessible through the Ziti overlay network or when you want to ensure\nall communication to the controller flows through the overlay for security purposes.\n\n### Identity Resolution Order\n\nWhen establishing connections, identities are resolved in the following order:\n\n1. **Command-line flag**: The `--network-identity` flag takes precedence\n2. **Environment variable**: If `ZITI_CLI_NETWORK_ID` is set and contains a base64-encoded identity, it is used\n3. **Cached identity file**: If a network identity was saved from a previous login in the Ziti config directory, it may be used\n\nThis layered approach allows for flexibility in deployment scenarios:\n- Development: Use command-line flags for quick testing\n- Automation: Use environment variables in CI/CD pipelines\n- Production: Cache identities securely for repeated access\n\n#### Dialing Modes When Authenticating\n\nThe CLI supports two dialing modes:\n\n**Intercept-based Dialing (Default)**\nBy default, URLs are expected to leverage intercepts. Create a service with an appropriate intercept config and use\nthe intercept address when dialing. This is the standard mode for most use cases. For example, given a service with\nthe intercept `ziti.mgmt.apis.local`\n```bash\nziti edge login https://ziti.mgmt.apis.local:1280 \\\n  --username myuser \\\n  --password mypass \\\n  --network-identity /path/to/identity.json\n```\n\n**Identity-aware Dialing (Addressable Terminators)**\nTo support addressable terminators-based dialing, specify a user in the URL. This activates dial-by-identity\nfunctionality. The URL format should be `identity-to-dial@service-name-to-dial`. For example:\n```bash\nziti edge login https://my-identity@my-service:1280 \\\n  --username myuser \\\n  --password mypass \\\n  --network-identity /path/to/identity.json\n```\n\nIn this mode, the transport extracts the identity from the URL and uses it to establish a direct connection to\nthe specified service via the addressable terminator.\n\n\n## OIDC/JWT Token-based Enrollment\n\nOpenZiti now supports provisioning identities just-in-time through OIDC/JWT token enrollment. External identity \nproviders can be configured to allow identities to enroll using JWT tokens, with support for the resulting \nidentities to use certificate or token authentication.\n\n### External JWT Signer Configuration\n\nExternal JWT signers are configured via the Edge Management API to define enrollment behavior with the following new \nenrollment-specific properties:\n\n- **enrollToCertEnabled** - When enabled, identities can exchange a JWT token and a certificate signing request (CSR) \n        for a client certificate during enrollment. The certificate can then be used for standard certificate-based\n        authentication.\n\n- **enrollToTokenEnabled** - When enabled, identities can use a JWT token to enroll. The current token or future tokens\n        may be used for authentication.\n\n- **enrollNameClaimsSelector** - Specifies which JWT claim contains the identity name. Accepts a JSON pointer \n        (e.g., `/preferred_username`) or a simple property name (e.g., `preferred_username`, automatically converted to\n        `/preferred_username`). Defaults to `/sub` if not specified. The extracted value becomes the identity name in Ziti.\n\n- **enrollAttributeClaimsSelector** - Specifies which JWT claims to extract as identity attributes during enrollment.\n        Accepts a JSON pointer (e.g., `/roles`) or a simple property name (e.g., `roles`). Extracted attributes are \n        applied to the newly enrolled identity for use in authorization policies.\n\n- **enrollAuthPolicyId** - Specifies the authentication policy to apply to newly enrolled identities. This determines\n        what authentication methods are available for the identity post-enrollment.\n\nAdditionally the existing property named **claimsProperty** that specifies external id to match identities to:\n\n- now supports a JSON pointer (e.g., `/id`) or a simple property name (e.g., `id`)\n- is used to populate the `externalId` field of the identity\n\n### Enrollment Paths\n\n#### Certificate Enrollment (enrollToCertEnabled)\n\nWhen certificate enrollment is enabled, unauthenticated users can:\n\n1. Obtain a list of available IdPs from the public Edge Client API `GET /external-jwt-signers` endpoint, where \n   `enrollToCertEnabled` is set to `true`\n2. Obtain a JWT from the configured OIDC provider\n3. Generate a certificate signing request (CSR)\n4. Submit an enrollment request with the JWT and CSR\n5. Have their identity created in Ziti with attributes extracted from JWT claims\n6. Receive a signed client certificate for certificate-based authentication\n\n#### Token Enrollment (enrollToTokenEnabled)\n\nWhen token enrollment is enabled, unauthenticated users can:\n\n1. Obtain a list of available IdPs from the public Edge Client API `GET /external-jwt-signers` endpoint, where \n   `enrollToTokenEnabled` is set to `true`\n1. Obtain a JWT from the configured OIDC provider\n2. Submit an enrollment request with the JWT\n3. Have their identity created in Ziti with attributes extracted from JWT claims\n4. Receive a Ziti API token for token-based authentication\n\n### Edge Management API\n\nThe Edge Management API provides full CRUD operations for configuring external JWT signers:\n\n- `POST /external-jwt-signers` - Create a new external JWT signer with all configuration options\n- `GET /external-jwt-signers` - List all configured external JWT signers\n- `GET /external-jwt-signers/{id}` - Retrieve a specific signer configuration\n- `PUT /external-jwt-signers/{id}` - Update all fields of a signer\n- `PATCH /external-jwt-signers/{id}` - Partially update a signer\n- `DELETE /external-jwt-signers/{id}` - Delete a signer\n\n### Edge Client API\n\nThe Edge Client API exposes a reduced set of external JWT signer information for unauthenticated enrollment requests:\n\n- `GET /external-jwt-signers` - List available JWT signers with enrollment capabilities\n\nThe client API response includes the following fields for each signer:\n\n- `name` - Signer name\n- `externalAuthUrl` - URL where users obtain JWT tokens\n- `clientId` - OIDC client ID\n- `scopes` - Requested OIDC scopes\n- `openIdConfigurationUrl` - OIDC discovery endpoint\n- `audience` - Expected token audience\n- `targetToken` - Token type to use (ACCESS or ID)\n- **`enrollToCertEnabled`** - Flag indicating certificate enrollment is available\n- **`enrollToTokenEnabled`** - Flag indicating token enrollment is available\n\n### CLI Commands\n\n**Create an external JWT signer with enrollment options:**\n```\nziti edge controller create ext-jwt-signer <name> <issuer> \\\n  --jwks-endpoint <url> \\\n  --audience <audience> \\\n  --enroll-to-cert \\\n  --enroll-to-token=false \\\n  --enroll-name-claims-selector preferred_username \\\n  --enroll-attr-claims-selector roles \\\n  --enroll-auth-policy <policy-id-or-name>\n```\n\n**Update enrollment options on an existing signer:**\n```\nziti edge controller update ext-jwt-signer <name|id> \\\n  --enroll-to-cert \\\n  --enroll-auth-policy <policy-id-or-name>\n```\n\n**List external JWT signers:**\n```\nziti edge controller list ext-jwt-signers\n```\n\n## Clustering Performance Improvements\n\nIn previous releases, model updates were submitted to raft one at at time. This prevented \nraft from being efficient by allowing command batching. This release allows multiple \nmodel updates to be in-flight at the same time. \n\nNew Configuration Options\n\n1. Raft Apply Timeout (cluster.applyTimeout)\n\nLocation: Controller configuration file, under the cluster section\nType: Duration\nDefault: 5s\nDescription: Timeout for applying commands to the Raft distributed log. Commands that exceed this timeout will trigger adaptive rate limiter backoff.\n\nExample:\n```\ncluster:\n  applyTimeout: 10s\n```\n\n2. Cluster Rate Limiter Configuration (cluster.rateLimiter)\n\nA new adaptive rate limiter that controls the submission of commands to the Raft cluster. Unlike the existing command rate limiter, this specifically manages in-flight Raft operations with adaptive window sizing.\n\nConfiguration Structure:\n```\ncluster:\n  rateLimiter:\n    enabled: true\n    minSize: 5\n    maxSize: 250\n    timeout: 30s\n```\n\nSub-options:\n\n  - enabled (boolean)\n    - Default: true\n    - Description: Enable/disable adaptive rate limiting for Raft command submission\n  - minSize (integer)\n    - Default: 5\n    - Minimum: 1\n    - Description: Minimum window size for concurrent in-flight Raft operations\n  - maxSize (integer)\n    - Default: 250\n    - Description: Maximum window size for concurrent in-flight Raft operations. Must be >= minSize\n  - timeout (duration)\n    - Default: 30s\n    - Description: Time after which outstanding work is assumed to have failed if not marked completed\n\n3. Restart Self on Snapshot (cluster.restartSelfOnSnapshot)\n\nLocation: Controller configuration file, under cluster section\nType: Boolean\nDefault: false\nDescription: When true, the controller will automatically restart itself when restoring a snapshot to an initialized system. When false, the controller will exit with code 0, requiring external process management to restart it.\n\nExample:\n```\ncluster:\n    restartSelfOnSnapshot: true\n```\n\n### New Metrics\n\nThe adaptive rate limiter exposes three new metrics:\n\n  1. raft.rate_limiter.queue_size (gauge)\n    - Current number of operations queued/in-flight\n  2. raft.rate_limiter.work_timer (timer)\n    - Duration of rate-limited operations\n  3. raft.rate_limiter.window_size (gauge)\n    - Current adaptive window size\n\n## Rate Limiter Algorithm Improvements\n\nThe adaptive rate limiter tracker now uses a success rate metric to decide when to grow or shrink its\nconcurrency window, instead of using raw queue position. An exponentially decaying histogram tracks the\nratio of successes to backoffs. When the success rate exceeds a configurable threshold, the window is\ngrown by a multiplicative increase factor. When it falls below the threshold, the window is shrunk by a\nmultiplicative decrease factor. The check intervals for increase and decrease are independently configurable.\n\nThis approach produces smoother, more stable window adjustments under varying load, avoiding the\nover-aggressive shrinking that could occur when queue position alone was used as the signal.\n\nThis specific rate limiter implementation is used in three places:\n* **Controller TLS handshake rate limiting** - controls the rate of incoming TLS handshakes on the controller\n* **Raft command submission** - controls the rate of commands submitted to the Raft distributed log\n* **Router control channel rate limiting** - controls the rate of requests from the router to the controller\n\nNote: this work was done in advance of enabling the TLS handshake rate limiter by default. The TLS\nhandshake rate limiter is not yet enabled by default, but can be enabled via the `tls.rateLimiter`\nconfiguration section.\n\nNew configuration options (available under each `rateLimiter` section):\n\n  - successThreshold (float)\n    - Default: 0.9\n    - Description: Success rate threshold above which the window size will be increased and below which it will be decreased\n  - increaseFactor (float)\n    - Default: 1.02\n    - Description: Multiplier applied to the current window size when growing. Must be greater than 1\n  - decreaseFactor (float)\n    - Default: 0.9\n    - Description: Multiplier applied to the current window size when shrinking. Must be between 0 and 1\n  - increaseCheckInterval (integer)\n    - Default: 10\n    - Description: Number of successes between window size increase checks\n  - decreaseCheckInterval (integer)\n    - Default: 10\n    - Description: Number of backoffs between window size decrease checks\n\n## Background Processing for Identity Updates\n\nIdentity environment and authenticator updates that occur during authentication are now processed asynchronously in the background. \nThis prevents authentication requests from blocking when the system is under load, significantly improving resilience during thundering herd scenarios.\n\nWhen the background queue fills up, updates can be dropped as they will be refreshed on the next authentication attempt. \nThis allows the system to gracefully handle load spikes without impacting authentication performance.\n\nFor now, dropping entries when the queue fills will be disabled by default, but can be enabled, see below.\n\n### Configuration\n\nA new `command.background` configuration section controls the background processing behavior:\n\n```yaml\n  command:\n    background:\n      enabled: true           # Enable background processing (default: true)\n      queueSize: 1000        # Maximum queue size (default: 1000)\n      dropWhenFull: true     # Drop updates when queue is full (default: false)\n      delayThreshold: 50ms   # The threshold for how long updates are taking before starting to background updates\n```\n\nNote that the `commandRateLimiter` configuration section may instead be specified under `command` as `rateLimiter`.\n\nExample:\n\n```yaml\n  command:\n    background:\n      enabled: true\n      queueSize: 250\n      dropWhenFull: false\n      delayThreshold: 50ms\n    rateLimiter:\n      enabled:   true\n      maxQueued: 25\n```\n\nNote that if command rate limiter configuration is specified in both locations, the settings under `command` will take \nprecedence. The standalone `commandRateLimiter` section may be deprecated in the future.\n\n### Metrics\n\nWhen background processing is enabled, the following metrics are exposed:\n\n- command.background.queue_size - Current number of queued background tasks\n- command.background.worker_count - Current number of worker goroutines\n- command.background.busy_workers - Number of workers currently processing tasks\n- command.background.work_timer - Timer tracking background task execution (includes histogram, meter, and count)\n- command.background.dropped_entries - Count of dropped updates when queue is full (only when dropWhenFull is enabled)\n\n## New proxy.v1 Config Type\n\n*Originally released in 1.7.0*\n\nAdded support for dynamic service proxies with configurable binding and protocol options.\nThis allows Edge Routers and Tunnelers to create proxy endpoints that can forward traffic for Ziti services.\n\nThis differs from intercept.v1 in that intercept.v1 will intercept traffic on specified\nIP addresses or DNS entries to forward to a service using tproxy or tun interface,\ndepending on implementation.\n\nA proxy on the other hand will just start a regular TCP/UDP listener on the configured port,\nso traffic will have to be configured for that destination.\n\nExample proxy.v1 Configuration:\n\n```\n  {\n    \"port\": 8080,\n    \"protocols\": [\"tcp\"],\n    \"binding\": \"0.0.0.0\"\n  }\n```\n\nConfiguration Properties:\n  - port (required): Port number to listen on (1-65535)\n  - protocols (required): Array of supported protocols (tcp, udp)\n  - binding (optional): Interface to bind to. For the ER/T defaults to the configured lanIF config property.\n\nThis config type is currently supported by the ER/T when running in either proxy or tproxy mode.\n\n## Alert Events (BETA)\n\n*Originally released in 1.7.0*\n\nA new alert event type has been added to allow Ziti components to emit alerts for issues that network operators can address.\nAlert events are generated when components encounter problems such as service configuration errors or resource\navailability issues.\n\nAlert events include:\n  - Alert source type and ID (currently supports routers, with controller and SDK support planned for future releases)\n  - Severity level (currently supports error, with info and warning planned for future releases)\n  - Alert message and supporting details\n  - Related entities (router, identity, service, etc.) associated with the alert\n\nExample alert event when a router cannot bind a configured network interface:\n\n```\n  {\n    \"namespace\": \"alert\",\n    \"event_src_id\": \"ctrl1\",\n    \"timestamp\": \"2021-11-08T14:45:45.785561479-05:00\",\n    \"alert_source_type\": \"router\",\n    \"alert_source_id\": \"DJFljCCoLs\",\n    \"severity\": \"error\",\n    \"message\": \"error starting proxy listener for service 'test'\",\n    \"details\": [\n      \"unable to bind eth0, no address\"\n    ],\n    \"related_entities\": {\n      \"router\": \"DJFljCCoLs\",\n      \"identity\": \"DJFljCCoLs\",\n      \"service\": \"3DPjxybDvXlo878CB0X2Zs\"\n    }\n  }\n```\n\nAlert events can be consumed through the standard event system and logged to configured event handlers for monitoring and alerting purposes.\n\nThese events are currently in Beta, as the format is still subject to change. Once they've been in use in production for a while\nand proven useful, they will be marked as stable.\n\n## Azure Service Bus Event Sink\n\n*Originally released in 1.7.0. Contributed by @ffaraone.*\n\nAdds support for streaming controller events to Azure Service Bus.\nThe new logger enables real-time event streaming from the OpenZiti controller to Azure Service Bus\nqueues or topics, providing integration with Azure-based monitoring and analytics systems.\n\nTo enable the Azure Service Bus event logger, add configuration to the controller config file under the events section:\n\n```\n  events:\n    serviceBusLogger:\n      subscriptions:\n        - type: circuit\n        - type: session\n        - type: metrics\n          sourceFilter: .*\n          metricFilter: .*\n        # Add other event types as needed\n      handler:\n        type: servicebus\n        format: json\n        connectionString: \"Endpoint=sb://your-namespace.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=your-key\"\n        topic: \"ziti-events\"          # Use 'topic' for Service Bus topic\n        # queue: \"ziti-events-queue\"  # Or use 'queue' for Service Bus queue\n        bufferSize: 100                # Optional, defaults to 50\n```\n\n- Required configuration:\n    - format: Event format, currently supports only json\n    - connectionString: Azure Service Bus connection string\n    - Either topic or queue: Destination name (mutually exclusive)\n\n- Optional configuration:\n    - bufferSize: Internal message buffer size (default: 50)\n\n## OIDC is now enabled by default\n\nThe controller now automatically adds the `edge-oidc` API binding to any web listener that hosts\nthe `edge-client` API, even when `edge-oidc` is not explicitly listed in the `web` section of the\nconfiguration. This means OIDC-based authentication is available out of the box without requiring\nchanges to existing controller configurations.\n\n### Where OIDC binds\n\nThe `edge-oidc` binding is added to the same web listener(s) as `edge-client`. If `edge-client` is\nhosted on `127.0.0.1:1280`, the OIDC endpoints will be available on that same address under the\n`/oidc` path (e.g. `https://127.0.0.1:1280/oidc/.well-known/openid-configuration`).\n\nThe controller capabilities returned by `GET /version` will include `OIDC_AUTH` and the\n`edge-oidc` entry will be present in `apiVersions` when OIDC is active.\n\n### Opting out\n\nIf OIDC should not be enabled automatically, set `disableOidcAutoBinding: true` under `edge.api`:\n\n```yaml\nedge:\n  api:\n    address: 127.0.0.1:1280\n    sessionTimeout: 30m\n    disableOidcAutoBinding: true\n```\n\nWhen `disableOidcAutoBinding` is `true`, OIDC will only be active if `edge-oidc` is explicitly\nlisted as a binding in the `web` section. \n\nWhen OIDC is not enabled, all (SDK) clients will revert to using legacy authentication.\nLegacy authentication does not support multiple controllers or HA in general. Clients will treat\ntheir controller as if it is a single controller instance and will function as if no other controllers\nexist.\n\n\n## WWW-Authenticate Headers\n\nThe controller's Edge APIs now include `WWW-Authenticate` headers on `401 Unauthorized` responses,\nper issuer: https://github.com/openziti/ziti/issues/3356. These headers provide insight into why\na token was rejected. The main benefit of these headers is to convey information for JWT backed\nAPI Sessions and API Session authentication where a token may not have been provided (missing),\nis used beyond its expiration date (expired), or the token has become invalid for any other\nreason (invalid).\n\n`www-authenticate` headers are provided as a single header instance with multiple challenge values\nseparate by commas.\n\n### No Credentials Provided\n\nWhen a request hits a protected endpoint without any credentials, a single `WWW-Authenticate` header\nis returned listing both accepted auth schemes as comma-separated challenges:\n\n```\nWWW-Authenticate: zt-session realm=\"zt-session\" error=\"missing\" error_description=\"no matching token was provided\",Bearer realm=\"openziti-oidc\" error=\"missing\" error_description=\"no matching token was provided\"\n```\n\n### Token Errors\n\nWhen a token is present but cannot be accepted, the header identifies the scheme and what went wrong:\n\n```\nWWW-Authenticate: Bearer realm=\"openziti-oidc\" error=\"expired\" error_description=\"token expired\"\nWWW-Authenticate: Bearer realm=\"openziti-oidc\" error=\"invalid\" error_description=\"token is invalid\"\nWWW-Authenticate: zt-session realm=\"zt-session\" error=\"invalid\" error_description=\"token is invalid\"\n```\n\n### OIDC External JWT — Primary Authentication\n\nWhen an auth policy requires an external JWT signer for primary authentication (e.g., a PKCE flow\nbacked by an ext-jwt signer), the header identifies which signers are accepted and what went wrong.\nMultiple accepted signers are pipe-delimited in the `id` and `issuer` parameters:\n\n```\nWWW-Authenticate: Bearer realm=\"openziti-primary-ext-jwt\" error=\"missing\" error_description=\"no matching token was provided\" id=\"signer-id-1|signer-id-2\" issuer=\"https://issuer1.example.com|https://issuer2.example.com\"\n```\n\nThe `error` value follows the same `missing`/`expired`/`invalid` pattern as standard bearer token errors.\n\n### OIDC External JWT — Secondary / MFA Authentication\n\nWhen an auth policy requires an external JWT signer as a secondary factor (step-up after primary\nauth succeeds), the header identifies the single required signer. The `error` value follows the\nsame `missing`/`expired`/`invalid` pattern:\n\n```\nWWW-Authenticate: Bearer realm=\"openziti-secondary-ext-jwt\" error=\"missing\" error_description=\"no matching token was provided\" id=\"<signer-id>\" issuer=\"<issuer>\"\n```\n\n### Anonymous Endpoints\n\nUnauthenticated endpoints such as version information do not return `WWW-Authenticate` headers.\n\n## HA Preferred Leaders\n\nControllers can be marked as a preferred leader. \n\n**Example Config**\n```yaml\ncluster:\n  dataDir: /home/{{ .Model.MustVariable \"credentials.ssh.username\" }}/fablab/ctrldata\n  preferredLeader: true\n```\n\nIf a controller that is not marked preferredLeader becomes a preferredLeader, it will check \nif there's a node available that is marked as preferred. If there is one, or one later\njoins the cluster, the non-preferred node will attempt to transfer leadership to the \nnode that is marked as preferred.\n\n## Expanded Dynamic Cost Range\n\nThe dynamic cost range for smart routing has been expanded beyond the previous 64K limit. Under high\nload, terminators could saturate the cost space, making dynamic cost values meaningless for routing\ndecisions and leading to uneven load distribution. The expanded range allows for more granular cost\ndifferentiation even under heavy load.\n\n## Circuit ID and Error in Dial Failures\n\nDial failures now return the circuit ID and, when available, the error that caused the circuit to fail.\nPreviously, the circuit ID was only returned on successful dials. Note that SDKs will need to be\nupdated to surface the circuit id when a dial failure happens.\n\n## Multi-Underlay Control Channels\n\nRouter-to-controller control channels now support multiple underlays with priority-based message routing.\nThis allows time-sensitive control messages (heartbeats, routing, circuit requests) to be separated from\noperational data (metrics, inspections) across dedicated TCP connections, preventing bulk operations from\ndelaying user-affecting control plane traffic. This feature does not yet allow specifying multiple \nnetwork interfaces to use, to load balance data across.\n\n## Dialing Identity Forwarded to Hosting SDK\n\nThe identity ID and name of the dialing client are now forwarded to the hosting SDK when a circuit is\nestablished. This allows hosting applications to identify which identity initiated the connection,\nenabling identity-aware request handling on the server side. This will require SDK updates to add this\nto the API for hosting applications.\n\n## Controller-Initiated Control Channel Dials (BETA)\n\nControllers can now dial routers to establish control channels. Previously, routers were solely\nresponsible for dialing controllers. This feature is designed for deployments where one or more\ncontrollers are in a private network that routers cannot reach, but the controllers can dial out.\nA common scenario is an HA cluster where some controllers are publicly reachable and some are in\na private network. External routers connect to the public controllers normally, and the private\ncontrollers dial out to the routers.\n\n### Router Configuration\n\nRouters can configure one or more control channel listeners. Each listener specifies a bind address,\nan advertise address (reported to the controller), and optional groups for matching.\n\n```yaml\nctrl:\n  listeners:\n    - bind: tls://0.0.0.0:6262\n      advertise: tls://router.example.com:6262\n      groups:\n        - default\n```\n\nThe router is the authoritative source of ctrl channel listener information, similar to link\nlisteners. When a router connects to any controller, it reports its configured `ctrlChanListeners`\nand the controller data model is updated automatically. This means that in most deployments —\nwhere the router can reach at least one controller — no manual configuration of listener addresses\nis needed on the controller side.\n\nThe `ctrlChanListeners` field can also be set via the CLI for cases where a router cannot reach\nany controller and thus cannot initialize the data model itself:\n\n```bash\nziti edge update edge-router myRouter --bootstrap-ctrl-chan-listener 'tls://router.example.com:6262=group1,group2'\n```\n\nGroups default to `[\"default\"]` if not specified.\n\n### Controller Configuration\n\nThe controller dialer is disabled by default and must be explicitly enabled. When enabled, the\ncontroller will dial routers that have control channel listeners configured and are not already\nconnected.\n\n```yaml\nctrl:\n  dialer:\n    enabled: true\n    groups:\n      - default\n    dialDelay: 30s\n    minRetryInterval: 1s\n    maxRetryInterval: 5m\n    retryBackoffFactor: 1.5\n    fastFailureWindow: 5s\n    queueSize: 32\n    maxWorkers: 10\n```\n\n- `enabled` - Enables the controller dialer (default: `false`)\n- `groups` - List of groups to match against router listener groups (default: `[\"default\"]`)\n- `dialDelay` - Delay before the controller starts dialing after boot (default: `30s`)\n- `minRetryInterval` - Minimum backoff delay between dial retries (default: `1s`)\n- `maxRetryInterval` - Maximum backoff delay between dial retries (default: `5m`)\n- `retryBackoffFactor` - Multiplier applied to the retry delay on each failure, jittered +/- 0.5 (default: `1.5`)\n- `fastFailureWindow` - If a connection drops within this window after connecting, backoff continues rather than resetting (default: `5s`)\n- `queueSize` - Maximum number of pending dial jobs in the worker pool queue (default: `32`)\n- `maxWorkers` - Maximum number of concurrent dial workers (default: `10`)\n\nThe controller will only dial routers whose listener groups overlap with the controller's configured\ngroups. When a router advertises multiple ctrl channel listener addresses, the dialer rotates through\nthem on each failure so that an unreachable address does not block attempts to the others.\n\n### Metrics\n\nThe controller dialer worker pool exposes the following metrics under the `ctrl_channel.dialer` prefix:\n\n- `ctrl_channel.dialer.queue_size` - Current number of pending dial jobs in the queue\n- `ctrl_channel.dialer.worker_count` - Current number of dial worker goroutines\n- `ctrl_channel.dialer.busy_workers` - Number of workers currently executing a dial\n- `ctrl_channel.dialer.work_timer` - Timer tracking the duration of each dial attempt\n\n## SDK Inspection Support\n\nNew CLI commands have been added for inspecting SDK state, useful for diagnosing terminator and\nconnectivity issues.\n\n**Note:** SDK inspection requires SDK support. Currently only the Go SDK supports inspection,\nas of version 1.5.0. Other SDKs will need to add support before these commands can be used\nwith them.\n\n### `ziti fabric inspect sdk`\n\nRetrieves SDK context inspection data from identities connected to routers.\n\n```\nziti fabric inspect sdk <target-selector> <identity-id>\n```\n\nThe `<target-selector>` is a regex matching router IDs (use `.*` for all routers). The\n`<identity-id>` is the identity whose SDK context you want to inspect. The command returns\ndetailed state from the connected SDK instance, including active services, terminators, and\nconnection status.\n\n### `ziti agent tunnel dump-sdk`\n\nDumps SDK context information from a running `ziti tunnel` process via the IPC agent.\n\n```\nziti agent tunnel dump-sdk\n```\n\nReturns JSON-formatted inspection data for all SDK contexts registered in the tunnel process,\nincluding service listeners, connections, and terminator state.\n\n## Current Beta Features\n\n* Basic Permission System\n* Alert Events\n* Controller-Initiated Control Channel Dials\n\n## Revocation System Improvements\n\nWhen a session is refreshed, the old refresh token's revocation is no longer created\nsynchronously through raft. Instead, revocations are queued in memory and flushed in\nbatches on a configurable interval. This removes the database and raft as a bottleneck\non token refreshes. If the old token is close to expiring, the revocation is skipped\nentirely.\n\nNew configuration tunables under `edge.oidc`:\n\n| Key | Default | Description |\n|-----|---------|-------------|\n| `revocationMinTokenLifetime` | unset | Skip revocation if the old token expires within this duration (must be < 50% of `refreshTokenDuration`) |\n| `revocationBucketInterval` | `1m` | Bucket window for batching revocations before flushing through raft |\n| `revocationBucketMaxSize` | `200` | Max revocations per raft entry |\n| `revocationMaxQueued` | `25000` | Max revocations queued in memory before dropping |\n| `revocationEnforcerFrequency` | `1m` | How often expired revocations are purged (leader only) |\n\n## Component Updates and Bug Fixes\n\n* github.com/openziti/agent: [v1.0.31 -> v1.0.33](https://github.com/openziti/agent/compare/v1.0.31...v1.0.33)\n* github.com/openziti/channel/v4: [v4.2.28 -> v4.3.9](https://github.com/openziti/channel/compare/v4.2.28...v4.3.9)\n    * [Issue #235](https://github.com/openziti/channel/issues/235) - Bump allowed hello message headers size to 16k from 4k\n    * [Issue #228](https://github.com/openziti/channel/issues/228) - Ensure that Underlay never return nil on MultiChannel\n    * [Issue #226](https://github.com/openziti/channel/issues/226) - Allow specifying a minimum number of underlays for a channel, regardless of underlay type\n    * [Issue #225](https://github.com/openziti/channel/issues/225) - Add ChannelCreated to the UnderlayHandler API to allow handlers to be initialized with the channel before binding\n    * [Issue #224](https://github.com/openziti/channel/issues/224) - Update the underlay dispatcher to allow unknown underlay types to fall through to the default\n    * [Issue #222](https://github.com/openziti/channel/issues/222) - Allow injecting the underlay type into messages\n\n* github.com/openziti/edge-api: [v0.26.47 -> v0.27.5](https://github.com/openziti/edge-api/compare/v0.26.47...v0.27.5)\n    * [Issue #175](https://github.com/openziti/edge-api/issues/175) - ctrlChanListeners should have x-omit-empty: false attribute\n    * [Issue #170](https://github.com/openziti/edge-api/issues/170) - Add preferredLeader flag to controllers\n    * [Issue #167](https://github.com/openziti/edge-api/issues/167) - Add ctrlChanListeners to router types\n    * [Issue #164](https://github.com/openziti/edge-api/issues/164) - Add permissions list to identity\n\n* github.com/openziti/foundation/v2: [v2.0.72 -> v2.0.90](https://github.com/openziti/foundation/compare/v2.0.72...v2.0.90)\n    * [Issue #472](https://github.com/openziti/foundation/issues/472) - Add support for multi-bit set/get to AtomicBitSet\n    * [Issue #464](https://github.com/openziti/foundation/issues/464) - Add support for -pre in versions\n    * [Issue #455](https://github.com/openziti/foundation/issues/455) - Correctly close goroutine pool when external close is signaled\n    * [Issue #452](https://github.com/openziti/foundation/issues/452) - Goroutine pool with a min worker count of 1 can drop to 0 workers due to race condition\n\n* github.com/openziti/identity: [v1.0.111 -> v1.0.128](https://github.com/openziti/identity/compare/v1.0.111...v1.0.128)\n    * [Issue #68](https://github.com/openziti/identity/issues/68) - Shutdown file watcher when stopping identity watcher\n\n* github.com/openziti/metrics: [v1.4.2 -> v1.4.5](https://github.com/openziti/metrics/compare/v1.4.2...v1.4.5)\n    * [Issue #58](https://github.com/openziti/metrics/issues/58) - Add GaugeFloat64 support\n    * [Issue #56](https://github.com/openziti/metrics/issues/56) - underlying resources of reference counted meters are not cleaned up when reference count hits zero\n\n* github.com/openziti/runzmd: [v1.0.80 -> v1.0.90](https://github.com/openziti/runzmd/compare/v1.0.80...v1.0.90)\n* github.com/openziti/sdk-golang: [v1.2.3 -> v1.6.0](https://github.com/openziti/sdk-golang/compare/v1.2.3...v1.6.0)\n    * [Issue #895](https://github.com/openziti/sdk-golang/issues/895) - Limit effect sudden rtt spikes can have on rtt moving average\n    * [Issue #902](https://github.com/openziti/sdk-golang/issues/902) - Inspect response message content types are mixed up\n    * [Issue #887](https://github.com/openziti/sdk-golang/issues/887) - Fix listener manager cleanup\n    * [Issue #886](https://github.com/openziti/sdk-golang/issues/886) - When controller is busy during service refresh, backoff and retry instead of falling back to full refresh\n    * [Issue #885](https://github.com/openziti/sdk-golang/issues/885) - Only compare relevant service fields when looking for changes\n    * [Issue #884](https://github.com/openziti/sdk-golang/issues/884) - Add deadline for bind establishment\n    * [Issue #883](https://github.com/openziti/sdk-golang/issues/883) - Router level listener can be left open if multi-listener closes during listener establishment\n    * [Issue #877](https://github.com/openziti/sdk-golang/issues/877) - Handle differences in xgress eof/end-of-circuit handling by adding a capabilities exchange\n    * [Issue #832](https://github.com/openziti/sdk-golang/issues/832) - Fuzz session refresh timers\n    * [Issue #879](https://github.com/openziti/sdk-golang/issues/879) - Return the connId in inspect response\n    * [Issue #878](https://github.com/openziti/sdk-golang/issues/878) - Fix responses from rx goroutines\n    * [Issue #874](https://github.com/openziti/sdk-golang/issues/874) - Add inspect support at the context level\n    * [Issue #871](https://github.com/openziti/sdk-golang/issues/871) - Make SDK better at sticking to MaxTerminator terminators\n    * [Issue #708](https://github.com/openziti/sdk-golang/issues/708) - Support for Go's built-in context in Dial methods\n    * [Issue #860](https://github.com/openziti/sdk-golang/issues/860) - Make the dialing identity's id and name available on dialed connections\n    * [Issue #857](https://github.com/openziti/sdk-golang/issues/857) - Use new error code and retry hints to correctly react to terminator errors\n    * [Issue #847](https://github.com/openziti/sdk-golang/issues/847) - Ensure the initial version check succeeds, to ensure we don't legacy sessions on ha or oidc-enabled controllers\n    * [Issue #824](https://github.com/openziti/sdk-golang/pull/824) - release notes and hard errors on no TOTP handler breaks partial auth events\n    * [Issue #818](https://github.com/openziti/sdk-golang/issues/818) - Full re-auth should not clear services list, as that breaks the on-change logic\n    * [Issue #817](https://github.com/openziti/sdk-golang/issues/817) - goroutines can get stuck when iterating over randomized HA controller list\n    * [Issue #736](https://github.com/openziti/sdk-golang/issues/736) - Migrate from github.com/mailru/easyjson\n    * [Issue #813](https://github.com/openziti/sdk-golang/issues/813) - SDK doesn't stop close listener when it detects that a service being hosted gets deleted\n    * [Issue #811](https://github.com/openziti/sdk-golang/issues/811) - Credentials are lost when explicitly set\n    * [Issue #807](https://github.com/openziti/sdk-golang/issues/807) - Don't send close from rxer to avoid blocking\n    * [Issue #800](https://github.com/openziti/sdk-golang/issues/800) - Tidy create service session logging\n\n* github.com/openziti/secretstream: [v0.1.39 -> v0.1.49](https://github.com/openziti/secretstream/compare/v0.1.39...v0.1.49)\n* github.com/openziti/storage: [v0.4.26 -> v0.4.39](https://github.com/openziti/storage/compare/v0.4.26...v0.4.39)\n    * [Issue #122](https://github.com/openziti/storage/issues/122) - StringFuncNode has incorrect nil check, allowing panic\n    * [Issue #120](https://github.com/openziti/storage/issues/120) - Change post tx commit constraint handling order\n    * [Issue #119](https://github.com/openziti/storage/issues/119) - Add ContextDecorator API\n\n* github.com/openziti/transport/v2: [v2.0.188 -> v2.0.215](https://github.com/openziti/transport/compare/v2.0.188...v2.0.215)\n    * [Issue #31](https://github.com/openziti/transport/issues/31) - ipv6 Transport Address Parsing\n    * [Issue #149](https://github.com/openziti/transport/issues/149) - Archive transwarp code\n\n* github.com/openziti/xweb/v3: [v2.3.4 -> v3.0.4](https://github.com/openziti/xweb/compare/v2.3.4...v3.0.4)\n    * [Issue #32](https://github.com/openziti/xweb/issues/32) - watched identities sometimes don't reload when changed\n\n* github.com/openziti/go-term-markdown: v1.0.1 (new)\n* github.com/openziti/ziti/v2: [v1.6.8 -> v2.0.0](https://github.com/openziti/ziti/compare/v1.6.8...v2.0.0)\n    * [Issue #3721](https://github.com/openziti/ziti/issues/3721) - Add CreateCircuitV3 to controller\n    * [Issue #3719](https://github.com/openziti/ziti/issues/3719) - Allow binding specific inspects to pass through to xgress listener implementations\n    * [Issue #3696](https://github.com/openziti/ziti/issues/3696) - oidc provider is non-deterministic for wildcard certs\n    * [Issue #3681](https://github.com/openziti/ziti/issues/3681) - coalesce OIDC JWT revocations to reduce controller write pressure\n    * [Issue #3683](https://github.com/openziti/ziti/issues/3683) - add fablab test for testing flow control changes over a longer term\n    * [Issue #3673](https://github.com/openziti/ziti/issues/3673) - revocation build-up in db and rdm\n    * [Issue #3674](https://github.com/openziti/ziti/issues/3674) - Update to Go 1.26\n    * [Issue #3496](https://github.com/openziti/ziti/issues/3496) - MFA TOTP Enrollment During OIDC Authentication Does Not Work\n    * [Issue #3609](https://github.com/openziti/ziti/issues/3609) - Stabilize terminator creation test for 2.0\n    * [Issue #3648](https://github.com/openziti/ziti/issues/3648) - tunnel: myCopy logs router ID as circuitId, causing misleading debug output\n    * [Issue #3658](https://github.com/openziti/ziti/issues/3658) - Raft cluster join fails with \"hello message too big\" when using long hostnames\n    * [Issue #3626](https://github.com/openziti/ziti/issues/3626) - controller: overlay bind point produces malformed URL in /versions apiBaseUrls\n    * [Issue #3635](https://github.com/openziti/ziti/issues/3635) - Allow controllers to dial routers to support more topologies\n    * [Issue #3607](https://github.com/openziti/ziti/issues/3607) - linux installer not upgradable from v1\n    * [Issue #3650](https://github.com/openziti/ziti/issues/3650) - Reroute doesn't proactively clean up orphaned route entries\n    * [Issue #3571](https://github.com/openziti/ziti/issues/3571) - Ensure 2.0 backwards compatibility with 1.6 and 1.5 using the smoketest\n    * [Issue #3636](https://github.com/openziti/ziti/issues/3636) - Adaptive rate limiter should use success rate rather than queue position\n    * [Issue #3600](https://github.com/openziti/ziti/issues/3600) - Add a preferredLeader flag, allow selected nodes to be preferred for raft leader, if they're available\n    * [Issue #3642](https://github.com/openziti/ziti/issues/3642) - Use xgress_common.Connection type for xgress_transport and xgress_proxy\n    * [Issue #3597](https://github.com/openziti/ziti/issues/3597) - Enable OIDC API by default\n    * [Issue #3624](https://github.com/openziti/ziti/issues/3624) - Multi-underlay control channel doesn't correctly handle lack of group secret on non-grouped underlays\n    * [Issue #3613](https://github.com/openziti/ziti/issues/3613) - Initializing cluster from existing db using `db:` config settings results in panic\n    * [Issue #3620](https://github.com/openziti/ziti/issues/3620) - Controller control channel heartbeats config parsing was erroneously nested under options parsing\n    * [Issue #3619](https://github.com/openziti/ziti/issues/3619) - Router connect events full sync interval was using min/max values meant for the batch interval\n    * [Issue #3618](https://github.com/openziti/ziti/issues/3618) - Router interfaceDiscovery.minReportInterval value was being set to checkInterval\n    * [Issue #3617](https://github.com/openziti/ziti/issues/3617) - Transit router disabled flag not passed through raft command structure\n    * [Issue #3333](https://github.com/openziti/ziti/issues/3333) - Updb user-lockout triggered by successful login attempts\n    * [Issue #3599](https://github.com/openziti/ziti/issues/3599) - Add gap detection and handling to router data model\n    * [Issue #3356](https://github.com/openziti/ziti/issues/3356) - Add WWW-Authenticate Headers\n    * [Issue #3074](https://github.com/openziti/ziti/issues/3074) - Dynamic cost range is too limited\n    * [Issue #3558](https://github.com/openziti/ziti/issues/3558) - terminator cost increased on egress dial success, not on circuit completion\n    * [Issue #3556](https://github.com/openziti/ziti/issues/3556) - global circuit costs not cleared when terminator is deleted\n    * [Issue #3557](https://github.com/openziti/ziti/issues/3557) - costing calculation for the weighted terminator selection strategy  is incorrect\n    * [Issue #2512](https://github.com/openziti/ziti/issues/2512) - Return circuit ID and error in dial failures\n    * [Issue #3569](https://github.com/openziti/ziti/issues/3569) - Version 2.0+ routers should not connect to controllers which do not support JWT formatted legacy sessions\n    * [Issue #3565](https://github.com/openziti/ziti/issues/3565) - Link dialer save 'is first conn' true, so all dials claim to be first, causing potential race condition\n    * [Issue #3550](https://github.com/openziti/ziti/issues/3550) - Support multi-underlay control channels\n    * [Issue #3535](https://github.com/openziti/ziti/issues/3535) - Remove the legacy xgress_edge_tunnel implementation\n    * [Issue #3547](https://github.com/openziti/ziti/issues/3547) - Add support for sending the dialing identity id and name to the hosting sdk\n    * [Issue #3541](https://github.com/openziti/ziti/issues/3541) - Remove option to disable the router data model in the controller\n    * [Issue #3540](https://github.com/openziti/ziti/issues/3540) - Handle UDP difference between proxy and tproxy implementations\n    * [Issue #3527](https://github.com/openziti/ziti/issues/3527) - ER/T UDP tunnels keep closed connections for 30s, preventing potential new good connections in that time\n    * [Issue #3526](https://github.com/openziti/ziti/issues/3526) - ER/T half-close logic is incorrect\n    * [Issue #3524](https://github.com/openziti/ziti/issues/3524) - Provide more error context to SDKs for terminator errors\n    * [Issue #3509](https://github.com/openziti/ziti/issues/3509) - Enforce policy on the router for oidc sessions, by closing open circuits and terminators when service access is lost\n    * [Issue #3531](https://github.com/openziti/ziti/issues/3531) - Remove created/updated/deleted terminator events. Obsoleted by entity change events.\n    * [Issue #3532](https://github.com/openziti/ziti/issues/3532) - Removed deprecated create identity <type> subcommands\n    * [Issue #3521](https://github.com/openziti/ziti/issues/3521) - Cleanup CLI to remove calls to os.Exit to be embed friendlier\n    * [Issue #3516](https://github.com/openziti/ziti/issues/3516) - Remove support for create terminator v1\n    * [Issue #3512](https://github.com/openziti/ziti/issues/3512) - Remove legacy link management code from the controller\n    * [Issue #3511](https://github.com/openziti/ziti/issues/3511) - router proxy mode fails to resolve interface if binding is 0.0.0.0\n    * [Issue #3503](https://github.com/openziti/ziti/issues/3503) - Allow routers to request current cluster membership information\n    * [Issue #3501](https://github.com/openziti/ziti/issues/3501) - Get cluster membership information from raft directly, rather than trying to cache it in the DB\n    * [Issue #3500](https://github.com/openziti/ziti/issues/3500) - Set a router data model timeline when initializing a new HA setup, rather than letting it stay blank\n    * [Issue #3504](https://github.com/openziti/ziti/issues/3504) - Reduce router data model full state updates\n    * [Issue #3492](https://github.com/openziti/ziti/pull/3492) - Bump openziti/ziti-console-assets from 3.12.9 to 4.0.0 in /dist/docker-images/ziti-controller in the all group\n    * [Issue #3484](https://github.com/openziti/ziti/issues/3484) - router ctrl channel handler for handling cluster changes has an initialization race condition\n    * [Issue #3477](https://github.com/openziti/ziti/issues/3477) - Optionally enable model changes triggered by login to be non-blocking and to be droppable if the system is under load\n    * [Issue #3473](https://github.com/openziti/ziti/issues/3473) - Enable tls handshake rate limiter by default and tweak default values.\n    * [Issue #3471](https://github.com/openziti/ziti/issues/3471) - Go tunneler is ignoring host config MaxConnections\n    * [Issue #3469](https://github.com/openziti/ziti/issues/3469) - Only send model updates on resubscribe if the RDM index has advanced\n    * [Issue #2573](https://github.com/openziti/ziti/issues/2573) - An edge router in a tight restart loop causes a resource leak on routers to which it connects.\n    * [Issue #3430](https://github.com/openziti/ziti/issues/3430) - Add permissions list to identity\n    * [Issue #2109](https://github.com/openziti/ziti/issues/2109) - Add Edge Management Read Only Capability\n    * [Issue #3435](https://github.com/openziti/ziti/issues/3435) - Add edge management API permissions by entity type and action\n    * [Issue #3441](https://github.com/openziti/ziti/issues/3441) - Update router connection tracker to interrogate active connections\n    * [Issue #3451](https://github.com/openziti/ziti/issues/3451) - ci - compare only stable releases when promoting\n    * [Issue #3437](https://github.com/openziti/ziti/issues/3437) - SDK OIDC token updates to routers should return an error if invalid\n    * [Issue #3348](https://github.com/openziti/ziti/issues/3348) - Unable to clear/reset the \"tags\" property on an entity to an empty object\n    * [Issue #3452](https://github.com/openziti/ziti/issues/3452) - `ziti agent cluster add` has bad behavior if the add address doesn't match the advertise address\n    * [Issue #3410](https://github.com/openziti/ziti/issues/3410) - Consolidate fabric REST API code with edge management and edge client code\n    * [Issue #3425](https://github.com/openziti/ziti/issues/3425) - RDM not properly responding to tunneler enabled flag changes\n    * [Issue #3420](https://github.com/openziti/ziti/issues/3420) - The terminator id cache uses the same id for all terminators in a host.v2 config, resulting in a single terminator\n    * [Issue #3419](https://github.com/openziti/ziti/issues/3419) - When using the router data model, precedence specified on the per-service identity mapping are incorrectly interpreted\n    * [Issue #3318](https://github.com/openziti/ziti/issues/3318) - Terminator creation seems to slow exponentially as the number of terminators rises from 10k to 20k to 40k\n    * [Issue #3407](https://github.com/openziti/ziti/issues/3407) - The CLI doesn't properly pass JWT authentication information to websocket endpoints\n    * [Issue #3359](https://github.com/openziti/ziti/issues/3359) - Ensure router data model subscriptions have reasonable performance and will scale\n    * [Issue #3354](https://github.com/openziti/ziti/issues/3354) - SDK/ENV Info from SDKs is not distributed in HA\n    * [Issue #3381](https://github.com/openziti/ziti/issues/3381) - the fabric service REST apis are missing the maxIdleTime property\n    * [Issue #3382](https://github.com/openziti/ziti/issues/3382) - Legacy service sessions generated pre-1.7.x are incompatible with v1.7.+ and need to be cleared\n    * [Issue #3339](https://github.com/openziti/ziti/issues/3339) - get router ctrl.endpoint from ctrls claim in JWT\n    * [Issue #3378](https://github.com/openziti/ziti/issues/3378) - login with file stopped working\n    * [Issue #3349](https://github.com/openziti/ziti/issues/3349) - UPDB OIDC login returns wrong content type\n    * [Issue #2324](https://github.com/openziti/ziti/issues/2324) - Add Ext-JWT/OIDC enrollment\n    * [Issue #3346](https://github.com/openziti/ziti/issues/3346) - Fix confusing attempt logging\n    * [Issue #3337](https://github.com/openziti/ziti/issues/3337) - Router reports \"no xgress edge forwarder for circuit\"\n    * [Issue #3345](https://github.com/openziti/ziti/issues/3345) - Clean up connect events tests and remove global XG registry\n    * [Issue #3264](https://github.com/openziti/ziti/issues/3264) - Allow routers to generate alert events in cases of service misconfiguration\n    * [Issue #3321](https://github.com/openziti/ziti/issues/3321) - Health Check API missing base path on discovery endpoint\n    * [Issue #3323](https://github.com/openziti/ziti/issues/3323) - router/tunnel static services fail to bind unless new param protocol is defined\n    * [Issue #3309](https://github.com/openziti/ziti/issues/3309) - Detect link connections meant for another router\n    * [Issue #3286](https://github.com/openziti/ziti/issues/3286) - edge-api binding doesn't have the correct path on discovery endpoints\n    * [Issue #3297](https://github.com/openziti/ziti/issues/3297) - stop promoting hotfixes downstream\n    * [Issue #3295](https://github.com/openziti/ziti/issues/3295) - make ziti tunnel service:port pairs optional\n    * [Issue #3291](https://github.com/openziti/ziti/issues/3291) - replace decommissioned bitnami/kubectl\n    * [Issue #3277](https://github.com/openziti/ziti/issues/3277) - Router can deadlock on closing a connection if the incoming data channel is full\n    * [Issue #3269](https://github.com/openziti/ziti/issues/3269) - Add host-interfaces config type\n    * [Issue #3258](https://github.com/openziti/ziti/issues/3258) - Add config type proxy.v1 so proxies can be defined dynamically for the ER/T\n    * [Issue #3259](https://github.com/openziti/ziti/issues/3259) - Interfaces config type not added due to wrong name\n    * [Issue #3265](https://github.com/openziti/ziti/issues/3265) - Forwarding errors should log at debug, since they are usual part of circuit teardown\n    * [Issue #3261](https://github.com/openziti/ziti/issues/3261) - ER/T dialed xgress connections may only half-close when peer is fully closed\n\n","mentions_count":1},{"url":"https://api.github.com/repos/openziti/ziti/releases/302520033","assets_url":"https://api.github.com/repos/openziti/ziti/releases/302520033/assets","upload_url":"https://uploads.github.com/repos/openziti/ziti/releases/302520033/assets{?name,label}","html_url":"https://github.com/openziti/ziti/releases/tag/v2.0.0-pre8","id":302520033,"author":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"node_id":"RE_kwDODVFMN84SCBbh","tag_name":"v2.0.0-pre8","target_commitish":"main","name":"v2.0.0-pre8","draft":false,"immutable":false,"prerelease":true,"created_at":"2026-03-27T19:06:35Z","updated_at":"2026-03-27T19:20:01Z","published_at":"2026-03-27T19:20:01Z","assets":[{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/383088633","id":383088633,"node_id":"RA_kwDODVFMN84W1Xf5","name":"checksums.sha256.txt","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"text/plain; charset=utf-8","state":"uploaded","size":790,"digest":"sha256:fca14973d8f4c895dac21c8502e1834c3fda8a7a466e807a26aa261145513c7e","download_count":6,"created_at":"2026-03-27T19:19:59Z","updated_at":"2026-03-27T19:20:00Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre8/checksums.sha256.txt"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/383088631","id":383088631,"node_id":"RA_kwDODVFMN84W1Xf3","name":"sbom-v2.0.0-pre8.spdx.json","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/json","state":"uploaded","size":1008832,"digest":"sha256:345779d071bb8b71eaa021ba9b0ad100b14de93471e3416996dfb8be5c71f36f","download_count":3,"created_at":"2026-03-27T19:19:59Z","updated_at":"2026-03-27T19:20:00Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre8/sbom-v2.0.0-pre8.spdx.json"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/383088632","id":383088632,"node_id":"RA_kwDODVFMN84W1Xf4","name":"source-v2.0.0-pre8.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":3787524,"digest":"sha256:ae81d348387fa12571a574e17b702c0a96f3e8347d62f0c81f334d9203081722","download_count":5,"created_at":"2026-03-27T19:19:59Z","updated_at":"2026-03-27T19:20:00Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre8/source-v2.0.0-pre8.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/383088603","id":383088603,"node_id":"RA_kwDODVFMN84W1Xfb","name":"ziti-darwin-amd64-2.0.0-pre8.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":53971952,"digest":"sha256:3179f588e743ec814d98c3c4649f8eff95f1830887eeb16a96bbb5ec9319950b","download_count":8,"created_at":"2026-03-27T19:19:57Z","updated_at":"2026-03-27T19:19:59Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre8/ziti-darwin-amd64-2.0.0-pre8.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/383088601","id":383088601,"node_id":"RA_kwDODVFMN84W1XfZ","name":"ziti-darwin-arm64-2.0.0-pre8.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":50326253,"digest":"sha256:76d35cf161990d9e7b0a08ce4aacfa7dbe28683fc1d21ecec2f1def8c5da3c5c","download_count":5,"created_at":"2026-03-27T19:19:57Z","updated_at":"2026-03-27T19:19:59Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre8/ziti-darwin-arm64-2.0.0-pre8.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/383088604","id":383088604,"node_id":"RA_kwDODVFMN84W1Xfc","name":"ziti-linux-amd64-2.0.0-pre8.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":55266937,"digest":"sha256:a9919d5e5d8d36df9f9c90864a5490e870d0e2d484c1471bf391b57517057546","download_count":18,"created_at":"2026-03-27T19:19:57Z","updated_at":"2026-03-27T19:19:59Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre8/ziti-linux-amd64-2.0.0-pre8.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/383088600","id":383088600,"node_id":"RA_kwDODVFMN84W1XfY","name":"ziti-linux-arm-2.0.0-pre8.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":51774085,"digest":"sha256:896dc6c343490c4caaae82eefae5fe9446e9359546ba1745158d99bbd5d92f84","download_count":7,"created_at":"2026-03-27T19:19:57Z","updated_at":"2026-03-27T19:19:59Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre8/ziti-linux-arm-2.0.0-pre8.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/383088629","id":383088629,"node_id":"RA_kwDODVFMN84W1Xf1","name":"ziti-linux-arm64-2.0.0-pre8.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":51777468,"digest":"sha256:75b409612bc516c554f1b3d8f18b1f28db98683aa157a3b55d62cc3f798e62fe","download_count":10,"created_at":"2026-03-27T19:19:59Z","updated_at":"2026-03-27T19:20:01Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre8/ziti-linux-arm64-2.0.0-pre8.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/383088602","id":383088602,"node_id":"RA_kwDODVFMN84W1Xfa","name":"ziti-windows-amd64-2.0.0-pre8.zip","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/zip","state":"uploaded","size":44651157,"digest":"sha256:b8ef21adf02f868b95bf2bb6e9694ec4b4548f85aee8d062eadf0bdce86a8c06","download_count":7,"created_at":"2026-03-27T19:19:57Z","updated_at":"2026-03-27T19:19:59Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre8/ziti-windows-amd64-2.0.0-pre8.zip"}],"tarball_url":"https://api.github.com/repos/openziti/ziti/tarball/v2.0.0-pre8","zipball_url":"https://api.github.com/repos/openziti/ziti/zipball/v2.0.0-pre8","body":"# Release 2.0.0\n\n## What's New\n\nThis is the next major version release of OpenZiti, following the 1.0 release in April 2024.\nOf particular note is that HA controllers are now considered ready for general use.\nThis release also introduces a new permissions model, OIDC/JWT token-based enrollment, \nclustering performance improvements, and a number of other features and fixes. Because \nsome of these changes are not backwards compatible with older routers, we're marking this \nas a major version bump.\n\n### HA Controllers are now considered ready for general use\n\nThis is a pretty big milestone and marks the completion of work that's been ongoing for a couple of years.\nThe HA work has brought with it some notable changes. Authentication now uses JWTs by default. Using JWTs\nmeans the controllers don't need to store session and propagate them to the routers. This removes a bottleneck\nfrom the network and allows the load to be more easily distributed among controllers and routers.\n\nTo support distributed authentication, the routers now get a bespoke version of the data model. In addition\nto enabling distributed authentication this will allow us to remove the need for service polling and further \nreduce the load on controllers in the future.\n\n### Router Compatibility\n\nRelated to the JWT work, routers with version 2.+ will only work with controllers that are version 2.+. This means\nto upgrade your network, controllers should be upgraded first. Routers can then be upgraded individually.\n\n2.x routers should still work fine with older router versions.\n\nWe try very hard to avoid breaking changes like this, but sometimes the engineering trade-offs lead there. This change\nwas first made in the 1.7 release. That release has not been marked stable, and we have no plans to do so, because\nof the backwards incompatibility. \n\nIf you need to support in the 1.6.12+ range, see the section \"OIDC enabled by default\".\n\n### New Permissions Model (BETA)\n\nAs one feature goes out of beta, another arrives into beta. This release introduces a new permissions system\nfor more fine grained control to the management API. It's not expected to change, but may do so based on feedback\nfrom users.\n\n### Updated Release Process\n\nWe have moved to creating `-preN` releases for major and minor versions. This way we can put out release candidates,\nor feature previews and put them through internal testing and let interested folks from the community try them out.\nThen, when we're ready, we can run the full validation suite against the last pre-release and retag it. \n\nPatch releases won't have `-preN` and should contain only high priority bug fixes.\n\n### Deprecation Cleanup\n\nSince we already have a breaking change, we're removing some other backwards compatibility code.\n\n* Controller managed links \n    * Router managed links were introduced in v0.30.0. \n    * If you're upgrading from an older versions, you'll want to upgrade to the latest 1.x release before jumping to 2.x\n    * Github tracking issue: https://github.com/openziti/ziti/issues/3512\n* `ziti edge create identity <type>`\n    * Identity types other than router were removed in v0.30.2\n    * The `type` can be dropped from the CLI command\n    * Github tracking issue: https://github.com/openziti/ziti/issues/3532\n* Terminator create/update/delete events\n    * These have been superseded by entity change events, which also have create/update/delete events for terminators\n    * Entity change events were introduced in v0.28.0\n    * Github tracking issue: https://github.com/openziti/ziti/issues/3531\n* `xgress_edge_tunnel` v1\n    * This is the first implementation of the tunneler in edge-router code (ER/T) which used legacy api sessions and services\n    * The v2 version uses the router data model and was introduced in v0.30.x\n    * Github tracking issue: https://github.com/openziti/ziti/issues/3516\n\n### Legacy Session Deprecation\n\nOIDC sessions are now preferred. They are the default, or will become the default for SDKs and tunnelers. They are also required\nwhen running HA. Legacy API and service session are now deprecated and will be removed in the OpenZiti v3.0.0 release. \n\n### Additional Features\n\n* Controllers can now optionally bind APIs using an OpenZiti identity\n* `ziti edge login` now supports the `--network-identity` flag to authenticate and establish connections through the Ziti overlay network\n* `ziti edge login` now supports using a bearer token with `--token` for authentication. The token is expected to be\n  provided as just the JWT, not with the \"Bearer \" prefix\n* Identity configuration can now be loaded from files or environment variables for flexible deployment scenarios\n* Identities can now be provisioned just-in-time through OIDC/JWT token-based enrollment\n* Multiple model updates can now be in-flight at the same time, improving clustering performance\n* Authentication-related model updates can now be non-blocking and even dropped if the system is too busy\n* Routers now provide more error context to SDKs for terminator errors, enabling better retry behavior\n* New `proxy.v1` config type for dynamic service proxies (originally released in 1.7.0)\n* New alert event type for surfacing operational issues to network operators - Beta (originally released in 1.7.0)\n* New Azure Service Bus event sink for streaming controller events, contributed by @ffaraone (originally released in 1.7.0)\n* Bundled ZAC upgraded to 4.0\n* Build updated to Go 1.25\n* CLI cleaned up to remove calls to `os.Exit`, making it more friendly for embedding\n* OIDC is now enabled by default\n* Controller Edge APIs now return `WWW-Authenticate` response headers on `401 Unauthorized` responses, giving clients actionable information about which auth methods are accepted and what went wrong\n* HA Controllers can be marked as 'preferredLeader' via config\n* Dynamic cost range for smart routing expanded beyond the previous 64K limit\n* Dial failures now return the circuit ID and error information for easier debugging\n* Router-to-controller control channels now support multiple underlays with priority-based message routing\n* The dialing identity's ID and name are now forwarded to the hosting SDK\n* Controllers can now dial routers to establish control channels, enabling connectivity when routers are behind firewalls (Beta)\n* Refresh-token revocations are now batched and best-effort, removing the database/raft bottleneck on token refreshes\n* `ziti edge quickstart` now always runs in HA mode. The `ha` subcommand has been removed. Use\n  `ziti edge quickstart join` to add additional members to the cluster. Note: existing quickstart instances\n  are not compatible with the new HA-only mode and will need to be recreated.\n* The `--clustered` flag on `ziti create config controller` has been removed; the generated config is always\n  cluster-ready. If you have scripts passing `--clustered`, remove it.\n\n## Basic Permission System (BETA)\n\nAdded a basic permission system that allows more control over identity access to controller management API operations. \nThis replaces the previous binary admin/non-admin model with a more flexible permission system.\n\n**NOTE:** This feature is in BETA, primarily so we can get feedback on which permissions make sense. The implementation is unlikely to change\nbut the set of exposed permissions may grow, shrink or change based on user feedback. \n\n### Permission Model\n\nThe permission system supports three levels of authorization:\n\n  1. **Global Permissions**: System-wide access levels\n     - `admin` - Full access to all operations. This is still controlled by the `isAdmin` flag on identity\n     - `admin_readonly` - Read-only access to all resources except debugging facilities inspect and validate\n\n  2. **Entity-Level Permissions**: Full CRUD access to specific entity types\n     - Granting an entity-level permission (e.g., `service`) provides complete create, read, update, and delete access for that entity type\n\n  3. **Action-Level Permissions**: Specific operation access on entity types\n     - Fine-grained control using the pattern `<entity>.<action>` (e.g., `service.read`, `identity.update`)\n     - Supports `create`, `read`, `update`, and `delete` actions per entity type\n\n### Supported Entity Permissions\n\nThe following entity-level permissions are available:\n\n- `auth-policy` - Authentication policy management\n- `ca` - Certificate Authority management\n- `config` - Configuration management\n- `config-type` - Configuration type management\n- `edge-router-policy` - Edge router policy management\n- `enrollment` - Enrollment management\n- `external-jwt-signer` - External JWT signer management\n- `identity` - Identity management\n- `posture-check` - Posture check management\n- `router` - Edge and transit router management\n- `service` - Service management\n- `service-policy` - Service policy management\n- `service-edge-router-policy` - Service edge router policy management\n- `terminator` - Terminator management\n- `ops` - Operational resources (API sessions, sessions, circuits, links, inspect and validate)\n\n### Permission Assignment\n\nPermissions are assigned to identities via the `permissions` field in the identity resource. Multiple permissions can be granted to a single identity, and permissions are additive.\n\n### Cross-Entity Operations\n\nListing related entities through an entity's endpoints requires appropriate permissions for the related entity type. For example:\n- Listing services for a service-policy requires `service.read` permission\n- Listing identities for an edge-router-policy requires `identity.read` permission\n- Listing configs for a service requires `config.read` permission\n\n**NOTE:** \nMore permissions than expected may be required when performing actions through the CLI or ZAC. Take for example, when an identity \nhas `config.create` and is attempting to create a new config. The CLI may fail if the identity doesn't have `config-type.read`\nas well because it will need to look up the config type id that corresponds to the given config type name.\n\nSimilar cross entity read permissions may be required when creating services.\n\n### Admin Protection\n\nNon-admin identities cannot:\n- Create identities with the `isAdmin` flag\n- Create identities with any permissions granted\n- Modify admin-related fields on existing identities\n- Update or delete admin identities\n- Grant permissions to identities\n\nThese protections ensure that privilege escalation is prevented and admin access remains controlled.\n\n\n## Binding Controller APIs With Identity\n\nController APIs can now be bound to an OpenZiti overlay network identity, allowing secure communication through\nthe Ziti network. This is useful for scenarios where you want to expose controller APIs only through the overlay\nnetwork rather than on a standard network interface.\n\n### Configuration Structure\n\nA standard `bindPoint` configuration looks like this:\n```text\n    bindPoints:\n      - interface: 127.0.0.1:18441\n        address: 127.0.0.1:18441\n```\n\nTo bind controller APIs to an OpenZiti identity, add an additional `identity` block to your `bindPoints`. The\nidentity configuration specifies where to load the Ziti identity file and which service to bind it to:\n\n```text\n    bindPoints:\n      - interface: 127.0.0.1:18441\n        address: 127.0.0.1:18441\n      - identity:\n          file: \"c:/temp/ctrl.testing/ctrl.identity.json\"\n          service: \"mgmt\"\n```\n\n### Supported Configuration Options\n\n- `file`: Path to a Ziti identity JSON file containing the controller's identity and enrollment certificate\n- `env`: Name of an environment variable containing a base64-encoded Ziti identity (alternative to `file`)\n- `service`: The name of the Ziti service to bind the controller API to\n\n### Using Environment Variables\n\nFor deployments where storing identity files on disk is not preferred, you can reference a base64-encoded\nidentity file from an environment variable. The environment variable should contain the base64-encoded contents\nof the identity JSON file.\n\nFor example, if an environment variable named `ZITI_CTRL_IDENTITY` contains a base64-encoded identity file:\n\n```text\n    bindPoints:\n      - interface: 127.0.0.1:18441\n        address: 127.0.0.1:18441\n      - identity:\n          env: ZITI_CTRL_IDENTITY\n          service: \"mgmt\"\n```\n\n### IPv6 Support\n\nBoth IPv4 and IPv6 addresses are supported for standard bind points. IPv6 addresses should be specified in bracket\nnotation with a port number:\n\n```text\n    bindPoints:\n      - interface: \"[::1]:18441\"\n        address: \"[::1]:18441\"\n      - identity:\n          file: \"/path/to/identity.json\"\n          service: \"mgmt\"\n```\n\n## CLI Enhancements for Identity-Based Connections\n\nThe `ziti edge login` command and REST client utilities have been enhanced to support identity-based connections\nthrough the Ziti overlay network.\n\n### New `--network-identity` Flag for `ziti edge login`\n\nThe `ziti edge login` command now includes a `--network-identity` flag that allows you to authenticate to a Ziti\ncontroller through the overlay network using a Ziti identity:\n\n```bash\nziti edge login https://ziti.mgmt.apis.local:1280 \\\n  --username myuser \\\n  --password mypass \\\n  --network-identity /path/to/identity.json\n```\n\nThis is useful when the controller is only accessible through the Ziti overlay network or when you want to ensure\nall communication to the controller flows through the overlay for security purposes.\n\n### Identity Resolution Order\n\nWhen establishing connections, identities are resolved in the following order:\n\n1. **Command-line flag**: The `--network-identity` flag takes precedence\n2. **Environment variable**: If `ZITI_CLI_NETWORK_ID` is set and contains a base64-encoded identity, it is used\n3. **Cached identity file**: If a network identity was saved from a previous login in the Ziti config directory, it may be used\n\nThis layered approach allows for flexibility in deployment scenarios:\n- Development: Use command-line flags for quick testing\n- Automation: Use environment variables in CI/CD pipelines\n- Production: Cache identities securely for repeated access\n\n#### Dialing Modes When Authenticating\n\nThe CLI supports two dialing modes:\n\n**Intercept-based Dialing (Default)**\nBy default, URLs are expected to leverage intercepts. Create a service with an appropriate intercept config and use\nthe intercept address when dialing. This is the standard mode for most use cases. For example, given a service with\nthe intercept `ziti.mgmt.apis.local`\n```bash\nziti edge login https://ziti.mgmt.apis.local:1280 \\\n  --username myuser \\\n  --password mypass \\\n  --network-identity /path/to/identity.json\n```\n\n**Identity-aware Dialing (Addressable Terminators)**\nTo support addressable terminators-based dialing, specify a user in the URL. This activates dial-by-identity\nfunctionality. The URL format should be `identity-to-dial@service-name-to-dial`. For example:\n```bash\nziti edge login https://my-identity@my-service:1280 \\\n  --username myuser \\\n  --password mypass \\\n  --network-identity /path/to/identity.json\n```\n\nIn this mode, the transport extracts the identity from the URL and uses it to establish a direct connection to\nthe specified service via the addressable terminator.\n\n\n## OIDC/JWT Token-based Enrollment\n\nOpenZiti now supports provisioning identities just-in-time through OIDC/JWT token enrollment. External identity \nproviders can be configured to allow identities to enroll using JWT tokens, with support for the resulting \nidentities to use certificate or token authentication.\n\n### External JWT Signer Configuration\n\nExternal JWT signers are configured via the Edge Management API to define enrollment behavior with the following new \nenrollment-specific properties:\n\n- **enrollToCertEnabled** - When enabled, identities can exchange a JWT token and a certificate signing request (CSR) \n        for a client certificate during enrollment. The certificate can then be used for standard certificate-based\n        authentication.\n\n- **enrollToTokenEnabled** - When enabled, identities can use a JWT token to enroll. The current token or future tokens\n        may be used for authentication.\n\n- **enrollNameClaimsSelector** - Specifies which JWT claim contains the identity name. Accepts a JSON pointer \n        (e.g., `/preferred_username`) or a simple property name (e.g., `preferred_username`, automatically converted to\n        `/preferred_username`). Defaults to `/sub` if not specified. The extracted value becomes the identity name in Ziti.\n\n- **enrollAttributeClaimsSelector** - Specifies which JWT claims to extract as identity attributes during enrollment.\n        Accepts a JSON pointer (e.g., `/roles`) or a simple property name (e.g., `roles`). Extracted attributes are \n        applied to the newly enrolled identity for use in authorization policies.\n\n- **enrollAuthPolicyId** - Specifies the authentication policy to apply to newly enrolled identities. This determines\n        what authentication methods are available for the identity post-enrollment.\n\nAdditionally the existing property named **claimsProperty** that specifies external id to match identities to:\n\n- now supports a JSON pointer (e.g., `/id`) or a simple property name (e.g., `id`)\n- is used to populate the `externalId` field of the identity\n\n### Enrollment Paths\n\n#### Certificate Enrollment (enrollToCertEnabled)\n\nWhen certificate enrollment is enabled, unauthenticated users can:\n\n1. Obtain a list of available IdPs from the public Edge Client API `GET /external-jwt-signers` endpoint, where \n   `enrollToCertEnabled` is set to `true`\n2. Obtain a JWT from the configured OIDC provider\n3. Generate a certificate signing request (CSR)\n4. Submit an enrollment request with the JWT and CSR\n5. Have their identity created in Ziti with attributes extracted from JWT claims\n6. Receive a signed client certificate for certificate-based authentication\n\n#### Token Enrollment (enrollToTokenEnabled)\n\nWhen token enrollment is enabled, unauthenticated users can:\n\n1. Obtain a list of available IdPs from the public Edge Client API `GET /external-jwt-signers` endpoint, where \n   `enrollToTokenEnabled` is set to `true`\n1. Obtain a JWT from the configured OIDC provider\n2. Submit an enrollment request with the JWT\n3. Have their identity created in Ziti with attributes extracted from JWT claims\n4. Receive a Ziti API token for token-based authentication\n\n### Edge Management API\n\nThe Edge Management API provides full CRUD operations for configuring external JWT signers:\n\n- `POST /external-jwt-signers` - Create a new external JWT signer with all configuration options\n- `GET /external-jwt-signers` - List all configured external JWT signers\n- `GET /external-jwt-signers/{id}` - Retrieve a specific signer configuration\n- `PUT /external-jwt-signers/{id}` - Update all fields of a signer\n- `PATCH /external-jwt-signers/{id}` - Partially update a signer\n- `DELETE /external-jwt-signers/{id}` - Delete a signer\n\n### Edge Client API\n\nThe Edge Client API exposes a reduced set of external JWT signer information for unauthenticated enrollment requests:\n\n- `GET /external-jwt-signers` - List available JWT signers with enrollment capabilities\n\nThe client API response includes the following fields for each signer:\n\n- `name` - Signer name\n- `externalAuthUrl` - URL where users obtain JWT tokens\n- `clientId` - OIDC client ID\n- `scopes` - Requested OIDC scopes\n- `openIdConfigurationUrl` - OIDC discovery endpoint\n- `audience` - Expected token audience\n- `targetToken` - Token type to use (ACCESS or ID)\n- **`enrollToCertEnabled`** - Flag indicating certificate enrollment is available\n- **`enrollToTokenEnabled`** - Flag indicating token enrollment is available\n\n### CLI Commands\n\n**Create an external JWT signer with enrollment options:**\n```\nziti edge controller create ext-jwt-signer <name> <issuer> \\\n  --jwks-endpoint <url> \\\n  --audience <audience> \\\n  --enroll-to-cert \\\n  --enroll-to-token=false \\\n  --enroll-name-claims-selector preferred_username \\\n  --enroll-attr-claims-selector roles \\\n  --enroll-auth-policy <policy-id-or-name>\n```\n\n**Update enrollment options on an existing signer:**\n```\nziti edge controller update ext-jwt-signer <name|id> \\\n  --enroll-to-cert \\\n  --enroll-auth-policy <policy-id-or-name>\n```\n\n**List external JWT signers:**\n```\nziti edge controller list ext-jwt-signers\n```\n\n## Clustering Performance Improvements\n\nIn previous releases, model updates were submitted to raft one at at time. This prevented \nraft from being efficient by allowing command batching. This release allows multiple \nmodel updates to be in-flight at the same time. \n\nNew Configuration Options\n\n1. Raft Apply Timeout (cluster.applyTimeout)\n\nLocation: Controller configuration file, under the cluster section\nType: Duration\nDefault: 5s\nDescription: Timeout for applying commands to the Raft distributed log. Commands that exceed this timeout will trigger adaptive rate limiter backoff.\n\nExample:\n```\ncluster:\n  applyTimeout: 10s\n```\n\n2. Cluster Rate Limiter Configuration (cluster.rateLimiter)\n\nA new adaptive rate limiter that controls the submission of commands to the Raft cluster. Unlike the existing command rate limiter, this specifically manages in-flight Raft operations with adaptive window sizing.\n\nConfiguration Structure:\n```\ncluster:\n  rateLimiter:\n    enabled: true\n    minSize: 5\n    maxSize: 250\n    timeout: 30s\n```\n\nSub-options:\n\n  - enabled (boolean)\n    - Default: true\n    - Description: Enable/disable adaptive rate limiting for Raft command submission\n  - minSize (integer)\n    - Default: 5\n    - Minimum: 1\n    - Description: Minimum window size for concurrent in-flight Raft operations\n  - maxSize (integer)\n    - Default: 250\n    - Description: Maximum window size for concurrent in-flight Raft operations. Must be >= minSize\n  - timeout (duration)\n    - Default: 30s\n    - Description: Time after which outstanding work is assumed to have failed if not marked completed\n\n3. Restart Self on Snapshot (cluster.restartSelfOnSnapshot)\n\nLocation: Controller configuration file, under cluster section\nType: Boolean\nDefault: false\nDescription: When true, the controller will automatically restart itself when restoring a snapshot to an initialized system. When false, the controller will exit with code 0, requiring external process management to restart it.\n\nExample:\n```\ncluster:\n    restartSelfOnSnapshot: true\n```\n\n### New Metrics\n\nThe adaptive rate limiter exposes three new metrics:\n\n  1. raft.rate_limiter.queue_size (gauge)\n    - Current number of operations queued/in-flight\n  2. raft.rate_limiter.work_timer (timer)\n    - Duration of rate-limited operations\n  3. raft.rate_limiter.window_size (gauge)\n    - Current adaptive window size\n\n## Rate Limiter Algorithm Improvements\n\nThe adaptive rate limiter tracker now uses a success rate metric to decide when to grow or shrink its\nconcurrency window, instead of using raw queue position. An exponentially decaying histogram tracks the\nratio of successes to backoffs. When the success rate exceeds a configurable threshold, the window is\ngrown by a multiplicative increase factor. When it falls below the threshold, the window is shrunk by a\nmultiplicative decrease factor. The check intervals for increase and decrease are independently configurable.\n\nThis approach produces smoother, more stable window adjustments under varying load, avoiding the\nover-aggressive shrinking that could occur when queue position alone was used as the signal.\n\nThis specific rate limiter implementation is used in three places:\n* **Controller TLS handshake rate limiting** - controls the rate of incoming TLS handshakes on the controller\n* **Raft command submission** - controls the rate of commands submitted to the Raft distributed log\n* **Router control channel rate limiting** - controls the rate of requests from the router to the controller\n\nNote: this work was done in advance of enabling the TLS handshake rate limiter by default. The TLS\nhandshake rate limiter is not yet enabled by default, but can be enabled via the `tls.rateLimiter`\nconfiguration section.\n\nNew configuration options (available under each `rateLimiter` section):\n\n  - successThreshold (float)\n    - Default: 0.9\n    - Description: Success rate threshold above which the window size will be increased and below which it will be decreased\n  - increaseFactor (float)\n    - Default: 1.02\n    - Description: Multiplier applied to the current window size when growing. Must be greater than 1\n  - decreaseFactor (float)\n    - Default: 0.9\n    - Description: Multiplier applied to the current window size when shrinking. Must be between 0 and 1\n  - increaseCheckInterval (integer)\n    - Default: 10\n    - Description: Number of successes between window size increase checks\n  - decreaseCheckInterval (integer)\n    - Default: 10\n    - Description: Number of backoffs between window size decrease checks\n\n## Background Processing for Identity Updates\n\nIdentity environment and authenticator updates that occur during authentication are now processed asynchronously in the background. \nThis prevents authentication requests from blocking when the system is under load, significantly improving resilience during thundering herd scenarios.\n\nWhen the background queue fills up, updates can be dropped as they will be refreshed on the next authentication attempt. \nThis allows the system to gracefully handle load spikes without impacting authentication performance.\n\nFor now, dropping entries when the queue fills will be disabled by default, but can be enabled, see below.\n\n### Configuration\n\nA new `command.background` configuration section controls the background processing behavior:\n\n```yaml\n  command:\n    background:\n      enabled: true           # Enable background processing (default: true)\n      queueSize: 1000        # Maximum queue size (default: 1000)\n      dropWhenFull: true     # Drop updates when queue is full (default: false)\n      delayThreshold: 50ms   # The threshold for how long updates are taking before starting to background updates\n```\n\nNote that the `commandRateLimiter` configuration section may instead be specified under `command` as `rateLimiter`.\n\nExample:\n\n```yaml\n  command:\n    background:\n      enabled: true\n      queueSize: 250\n      dropWhenFull: false\n      delayThreshold: 50ms\n    rateLimiter:\n      enabled:   true\n      maxQueued: 25\n```\n\nNote that if command rate limiter configuration is specified in both locations, the settings under `command` will take \nprecedence. The standalone `commandRateLimiter` section may be deprecated in the future.\n\n### Metrics\n\nWhen background processing is enabled, the following metrics are exposed:\n\n- command.background.queue_size - Current number of queued background tasks\n- command.background.worker_count - Current number of worker goroutines\n- command.background.busy_workers - Number of workers currently processing tasks\n- command.background.work_timer - Timer tracking background task execution (includes histogram, meter, and count)\n- command.background.dropped_entries - Count of dropped updates when queue is full (only when dropWhenFull is enabled)\n\n## New proxy.v1 Config Type\n\n*Originally released in 1.7.0*\n\nAdded support for dynamic service proxies with configurable binding and protocol options.\nThis allows Edge Routers and Tunnelers to create proxy endpoints that can forward traffic for Ziti services.\n\nThis differs from intercept.v1 in that intercept.v1 will intercept traffic on specified\nIP addresses or DNS entries to forward to a service using tproxy or tun interface,\ndepending on implementation.\n\nA proxy on the other hand will just start a regular TCP/UDP listener on the configured port,\nso traffic will have to be configured for that destination.\n\nExample proxy.v1 Configuration:\n\n```\n  {\n    \"port\": 8080,\n    \"protocols\": [\"tcp\"],\n    \"binding\": \"0.0.0.0\"\n  }\n```\n\nConfiguration Properties:\n  - port (required): Port number to listen on (1-65535)\n  - protocols (required): Array of supported protocols (tcp, udp)\n  - binding (optional): Interface to bind to. For the ER/T defaults to the configured lanIF config property.\n\nThis config type is currently supported by the ER/T when running in either proxy or tproxy mode.\n\n## Alert Events (BETA)\n\n*Originally released in 1.7.0*\n\nA new alert event type has been added to allow Ziti components to emit alerts for issues that network operators can address.\nAlert events are generated when components encounter problems such as service configuration errors or resource\navailability issues.\n\nAlert events include:\n  - Alert source type and ID (currently supports routers, with controller and SDK support planned for future releases)\n  - Severity level (currently supports error, with info and warning planned for future releases)\n  - Alert message and supporting details\n  - Related entities (router, identity, service, etc.) associated with the alert\n\nExample alert event when a router cannot bind a configured network interface:\n\n```\n  {\n    \"namespace\": \"alert\",\n    \"event_src_id\": \"ctrl1\",\n    \"timestamp\": \"2021-11-08T14:45:45.785561479-05:00\",\n    \"alert_source_type\": \"router\",\n    \"alert_source_id\": \"DJFljCCoLs\",\n    \"severity\": \"error\",\n    \"message\": \"error starting proxy listener for service 'test'\",\n    \"details\": [\n      \"unable to bind eth0, no address\"\n    ],\n    \"related_entities\": {\n      \"router\": \"DJFljCCoLs\",\n      \"identity\": \"DJFljCCoLs\",\n      \"service\": \"3DPjxybDvXlo878CB0X2Zs\"\n    }\n  }\n```\n\nAlert events can be consumed through the standard event system and logged to configured event handlers for monitoring and alerting purposes.\n\nThese events are currently in Beta, as the format is still subject to change. Once they've been in use in production for a while\nand proven useful, they will be marked as stable.\n\n## Azure Service Bus Event Sink\n\n*Originally released in 1.7.0. Contributed by @ffaraone.*\n\nAdds support for streaming controller events to Azure Service Bus.\nThe new logger enables real-time event streaming from the OpenZiti controller to Azure Service Bus\nqueues or topics, providing integration with Azure-based monitoring and analytics systems.\n\nTo enable the Azure Service Bus event logger, add configuration to the controller config file under the events section:\n\n```\n  events:\n    serviceBusLogger:\n      subscriptions:\n        - type: circuit\n        - type: session\n        - type: metrics\n          sourceFilter: .*\n          metricFilter: .*\n        # Add other event types as needed\n      handler:\n        type: servicebus\n        format: json\n        connectionString: \"Endpoint=sb://your-namespace.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=your-key\"\n        topic: \"ziti-events\"          # Use 'topic' for Service Bus topic\n        # queue: \"ziti-events-queue\"  # Or use 'queue' for Service Bus queue\n        bufferSize: 100                # Optional, defaults to 50\n```\n\n- Required configuration:\n    - format: Event format, currently supports only json\n    - connectionString: Azure Service Bus connection string\n    - Either topic or queue: Destination name (mutually exclusive)\n\n- Optional configuration:\n    - bufferSize: Internal message buffer size (default: 50)\n\n## OIDC is now enabled by default\n\nThe controller now automatically adds the `edge-oidc` API binding to any web listener that hosts\nthe `edge-client` API, even when `edge-oidc` is not explicitly listed in the `web` section of the\nconfiguration. This means OIDC-based authentication is available out of the box without requiring\nchanges to existing controller configurations.\n\n### Where OIDC binds\n\nThe `edge-oidc` binding is added to the same web listener(s) as `edge-client`. If `edge-client` is\nhosted on `127.0.0.1:1280`, the OIDC endpoints will be available on that same address under the\n`/oidc` path (e.g. `https://127.0.0.1:1280/oidc/.well-known/openid-configuration`).\n\nThe controller capabilities returned by `GET /version` will include `OIDC_AUTH` and the\n`edge-oidc` entry will be present in `apiVersions` when OIDC is active.\n\n### Opting out\n\nIf OIDC should not be enabled automatically, set `disableOidcAutoBinding: true` under `edge.api`:\n\n```yaml\nedge:\n  api:\n    address: 127.0.0.1:1280\n    sessionTimeout: 30m\n    disableOidcAutoBinding: true\n```\n\nWhen `disableOidcAutoBinding` is `true`, OIDC will only be active if `edge-oidc` is explicitly\nlisted as a binding in the `web` section. \n\nWhen OIDC is not enabled, all (SDK) clients will revert to using legacy authentication.\nLegacy authentication does not support multiple controllers or HA in general. Clients will treat\ntheir controller as if it is a single controller instance and will function as if no other controllers\nexist.\n\n\n## WWW-Authenticate Headers\n\nThe controller's Edge APIs now include `WWW-Authenticate` headers on `401 Unauthorized` responses,\nper issuer: https://github.com/openziti/ziti/issues/3356. These headers provide insight into why\na token was rejected. The main benefit of these headers is to convey information for JWT backed\nAPI Sessions and API Session authentication where a token may not have been provided (missing),\nis used beyond its expiration date (expired), or the token has become invalid for any other\nreason (invalid).\n\n`www-authenticate` headers are provided as a single header instance with multiple challenge values\nseparate by commas.\n\n### No Credentials Provided\n\nWhen a request hits a protected endpoint without any credentials, a single `WWW-Authenticate` header\nis returned listing both accepted auth schemes as comma-separated challenges:\n\n```\nWWW-Authenticate: zt-session realm=\"zt-session\" error=\"missing\" error_description=\"no matching token was provided\",Bearer realm=\"openziti-oidc\" error=\"missing\" error_description=\"no matching token was provided\"\n```\n\n### Token Errors\n\nWhen a token is present but cannot be accepted, the header identifies the scheme and what went wrong:\n\n```\nWWW-Authenticate: Bearer realm=\"openziti-oidc\" error=\"expired\" error_description=\"token expired\"\nWWW-Authenticate: Bearer realm=\"openziti-oidc\" error=\"invalid\" error_description=\"token is invalid\"\nWWW-Authenticate: zt-session realm=\"zt-session\" error=\"invalid\" error_description=\"token is invalid\"\n```\n\n### OIDC External JWT — Primary Authentication\n\nWhen an auth policy requires an external JWT signer for primary authentication (e.g., a PKCE flow\nbacked by an ext-jwt signer), the header identifies which signers are accepted and what went wrong.\nMultiple accepted signers are pipe-delimited in the `id` and `issuer` parameters:\n\n```\nWWW-Authenticate: Bearer realm=\"openziti-primary-ext-jwt\" error=\"missing\" error_description=\"no matching token was provided\" id=\"signer-id-1|signer-id-2\" issuer=\"https://issuer1.example.com|https://issuer2.example.com\"\n```\n\nThe `error` value follows the same `missing`/`expired`/`invalid` pattern as standard bearer token errors.\n\n### OIDC External JWT — Secondary / MFA Authentication\n\nWhen an auth policy requires an external JWT signer as a secondary factor (step-up after primary\nauth succeeds), the header identifies the single required signer. The `error` value follows the\nsame `missing`/`expired`/`invalid` pattern:\n\n```\nWWW-Authenticate: Bearer realm=\"openziti-secondary-ext-jwt\" error=\"missing\" error_description=\"no matching token was provided\" id=\"<signer-id>\" issuer=\"<issuer>\"\n```\n\n### Anonymous Endpoints\n\nUnauthenticated endpoints such as version information do not return `WWW-Authenticate` headers.\n\n## HA Preferred Leaders\n\nControllers can be marked as a preferred leader. \n\n**Example Config**\n```yaml\ncluster:\n  dataDir: /home/{{ .Model.MustVariable \"credentials.ssh.username\" }}/fablab/ctrldata\n  preferredLeader: true\n```\n\nIf a controller that is not marked preferredLeader becomes a preferredLeader, it will check \nif there's a node available that is marked as preferred. If there is one, or one later\njoins the cluster, the non-preferred node will attempt to transfer leadership to the \nnode that is marked as preferred.\n\n## Expanded Dynamic Cost Range\n\nThe dynamic cost range for smart routing has been expanded beyond the previous 64K limit. Under high\nload, terminators could saturate the cost space, making dynamic cost values meaningless for routing\ndecisions and leading to uneven load distribution. The expanded range allows for more granular cost\ndifferentiation even under heavy load.\n\n## Circuit ID and Error in Dial Failures\n\nDial failures now return the circuit ID and, when available, the error that caused the circuit to fail.\nPreviously, the circuit ID was only returned on successful dials. Note that SDKs will need to be\nupdated to surface the circuit id when a dial failure happens.\n\n## Multi-Underlay Control Channels\n\nRouter-to-controller control channels now support multiple underlays with priority-based message routing.\nThis allows time-sensitive control messages (heartbeats, routing, circuit requests) to be separated from\noperational data (metrics, inspections) across dedicated TCP connections, preventing bulk operations from\ndelaying user-affecting control plane traffic. This feature does not yet allow specifying multiple \nnetwork interfaces to use, to load balance data across.\n\n## Dialing Identity Forwarded to Hosting SDK\n\nThe identity ID and name of the dialing client are now forwarded to the hosting SDK when a circuit is\nestablished. This allows hosting applications to identify which identity initiated the connection,\nenabling identity-aware request handling on the server side. This will require SDK updates to add this\nto the API for hosting applications.\n\n## Controller-Initiated Control Channel Dials (BETA)\n\nControllers can now dial routers to establish control channels. Previously, routers were solely\nresponsible for dialing controllers. This feature is designed for deployments where one or more\ncontrollers are in a private network that routers cannot reach, but the controllers can dial out.\nA common scenario is an HA cluster where some controllers are publicly reachable and some are in\na private network. External routers connect to the public controllers normally, and the private\ncontrollers dial out to the routers.\n\n### Router Configuration\n\nRouters can configure one or more control channel listeners. Each listener specifies a bind address,\nan advertise address (reported to the controller), and optional groups for matching.\n\n```yaml\nctrl:\n  listeners:\n    - bind: tls://0.0.0.0:6262\n      advertise: tls://router.example.com:6262\n      groups:\n        - default\n```\n\nThe router is the authoritative source of ctrl channel listener information, similar to link\nlisteners. When a router connects to any controller, it reports its configured `ctrlChanListeners`\nand the controller data model is updated automatically. This means that in most deployments —\nwhere the router can reach at least one controller — no manual configuration of listener addresses\nis needed on the controller side.\n\nThe `ctrlChanListeners` field can also be set via the CLI for cases where a router cannot reach\nany controller and thus cannot initialize the data model itself:\n\n```bash\nziti edge update edge-router myRouter --bootstrap-ctrl-chan-listener 'tls://router.example.com:6262=group1,group2'\n```\n\nGroups default to `[\"default\"]` if not specified.\n\n### Controller Configuration\n\nThe controller dialer is disabled by default and must be explicitly enabled. When enabled, the\ncontroller will dial routers that have control channel listeners configured and are not already\nconnected.\n\n```yaml\nctrl:\n  dialer:\n    enabled: true\n    groups:\n      - default\n    dialDelay: 30s\n    minRetryInterval: 1s\n    maxRetryInterval: 5m\n    retryBackoffFactor: 1.5\n    fastFailureWindow: 5s\n    queueSize: 32\n    maxWorkers: 10\n```\n\n- `enabled` - Enables the controller dialer (default: `false`)\n- `groups` - List of groups to match against router listener groups (default: `[\"default\"]`)\n- `dialDelay` - Delay before the controller starts dialing after boot (default: `30s`)\n- `minRetryInterval` - Minimum backoff delay between dial retries (default: `1s`)\n- `maxRetryInterval` - Maximum backoff delay between dial retries (default: `5m`)\n- `retryBackoffFactor` - Multiplier applied to the retry delay on each failure, jittered +/- 0.5 (default: `1.5`)\n- `fastFailureWindow` - If a connection drops within this window after connecting, backoff continues rather than resetting (default: `5s`)\n- `queueSize` - Maximum number of pending dial jobs in the worker pool queue (default: `32`)\n- `maxWorkers` - Maximum number of concurrent dial workers (default: `10`)\n\nThe controller will only dial routers whose listener groups overlap with the controller's configured\ngroups. When a router advertises multiple ctrl channel listener addresses, the dialer rotates through\nthem on each failure so that an unreachable address does not block attempts to the others.\n\n### Metrics\n\nThe controller dialer worker pool exposes the following metrics under the `ctrl_channel.dialer` prefix:\n\n- `ctrl_channel.dialer.queue_size` - Current number of pending dial jobs in the queue\n- `ctrl_channel.dialer.worker_count` - Current number of dial worker goroutines\n- `ctrl_channel.dialer.busy_workers` - Number of workers currently executing a dial\n- `ctrl_channel.dialer.work_timer` - Timer tracking the duration of each dial attempt\n\n## SDK Inspection Support\n\nNew CLI commands have been added for inspecting SDK state, useful for diagnosing terminator and\nconnectivity issues.\n\n**Note:** SDK inspection requires SDK support. Currently only the Go SDK supports inspection,\nas of version 1.5.0. Other SDKs will need to add support before these commands can be used\nwith them.\n\n### `ziti fabric inspect sdk`\n\nRetrieves SDK context inspection data from identities connected to routers.\n\n```\nziti fabric inspect sdk <target-selector> <identity-id>\n```\n\nThe `<target-selector>` is a regex matching router IDs (use `.*` for all routers). The\n`<identity-id>` is the identity whose SDK context you want to inspect. The command returns\ndetailed state from the connected SDK instance, including active services, terminators, and\nconnection status.\n\n### `ziti agent tunnel dump-sdk`\n\nDumps SDK context information from a running `ziti tunnel` process via the IPC agent.\n\n```\nziti agent tunnel dump-sdk\n```\n\nReturns JSON-formatted inspection data for all SDK contexts registered in the tunnel process,\nincluding service listeners, connections, and terminator state.\n\n## Current Beta Features\n\n* Basic Permission System\n* Alert Events\n* Controller-Initiated Control Channel Dials\n\n## Revocation System Improvements\n\nWhen a session is refreshed, the old refresh token's revocation is no longer created\nsynchronously through raft. Instead, revocations are queued in memory and flushed in\nbatches on a configurable interval. This removes the database and raft as a bottleneck\non token refreshes. If the old token is close to expiring, the revocation is skipped\nentirely.\n\nNew configuration tunables under `edge.oidc`:\n\n| Key | Default | Description |\n|-----|---------|-------------|\n| `revocationMinTokenLifetime` | unset | Skip revocation if the old token expires within this duration (must be < 50% of `refreshTokenDuration`) |\n| `revocationBucketInterval` | `1m` | Bucket window for batching revocations before flushing through raft |\n| `revocationBucketMaxSize` | `200` | Max revocations per raft entry |\n| `revocationMaxQueued` | `25000` | Max revocations queued in memory before dropping |\n| `revocationEnforcerFrequency` | `1m` | How often expired revocations are purged (leader only) |\n\n## Component Updates and Bug Fixes\n\n* github.com/openziti/agent: [v1.0.31 -> v1.0.33](https://github.com/openziti/agent/compare/v1.0.31...v1.0.33)\n* github.com/openziti/channel/v4: [v4.2.28 -> v4.3.9](https://github.com/openziti/channel/compare/v4.2.28...v4.3.9)\n    * [Issue #235](https://github.com/openziti/channel/issues/235) - Bump allowed hello message headers size to 16k from 4k\n    * [Issue #228](https://github.com/openziti/channel/issues/228) - Ensure that Underlay never return nil on MultiChannel\n    * [Issue #226](https://github.com/openziti/channel/issues/226) - Allow specifying a minimum number of underlays for a channel, regardless of underlay type\n    * [Issue #225](https://github.com/openziti/channel/issues/225) - Add ChannelCreated to the UnderlayHandler API to allow handlers to be initialized with the channel before binding\n    * [Issue #224](https://github.com/openziti/channel/issues/224) - Update the underlay dispatcher to allow unknown underlay types to fall through to the default\n    * [Issue #222](https://github.com/openziti/channel/issues/222) - Allow injecting the underlay type into messages\n\n* github.com/openziti/edge-api: [v0.26.47 -> v0.27.5](https://github.com/openziti/edge-api/compare/v0.26.47...v0.27.5)\n    * [Issue #175](https://github.com/openziti/edge-api/issues/175) - ctrlChanListeners should have x-omit-empty: false attribute\n    * [Issue #170](https://github.com/openziti/edge-api/issues/170) - Add preferredLeader flag to controllers\n    * [Issue #167](https://github.com/openziti/edge-api/issues/167) - Add ctrlChanListeners to router types\n    * [Issue #164](https://github.com/openziti/edge-api/issues/164) - Add permissions list to identity\n\n* github.com/openziti/foundation/v2: [v2.0.72 -> v2.0.90](https://github.com/openziti/foundation/compare/v2.0.72...v2.0.90)\n    * [Issue #472](https://github.com/openziti/foundation/issues/472) - Add support for multi-bit set/get to AtomicBitSet\n    * [Issue #464](https://github.com/openziti/foundation/issues/464) - Add support for -pre in versions\n    * [Issue #455](https://github.com/openziti/foundation/issues/455) - Correctly close goroutine pool when external close is signaled\n    * [Issue #452](https://github.com/openziti/foundation/issues/452) - Goroutine pool with a min worker count of 1 can drop to 0 workers due to race condition\n\n* github.com/openziti/identity: [v1.0.111 -> v1.0.128](https://github.com/openziti/identity/compare/v1.0.111...v1.0.128)\n    * [Issue #68](https://github.com/openziti/identity/issues/68) - Shutdown file watcher when stopping identity watcher\n\n* github.com/openziti/metrics: [v1.4.2 -> v1.4.5](https://github.com/openziti/metrics/compare/v1.4.2...v1.4.5)\n    * [Issue #58](https://github.com/openziti/metrics/issues/58) - Add GaugeFloat64 support\n    * [Issue #56](https://github.com/openziti/metrics/issues/56) - underlying resources of reference counted meters are not cleaned up when reference count hits zero\n\n* github.com/openziti/runzmd: [v1.0.80 -> v1.0.90](https://github.com/openziti/runzmd/compare/v1.0.80...v1.0.90)\n* github.com/openziti/sdk-golang: [v1.2.3 -> v1.6.0](https://github.com/openziti/sdk-golang/compare/v1.2.3...v1.6.0)\n    * [Issue #895](https://github.com/openziti/sdk-golang/issues/895) - Limit effect sudden rtt spikes can have on rtt moving average\n    * [Issue #902](https://github.com/openziti/sdk-golang/issues/902) - Inspect response message content types are mixed up\n    * [Issue #887](https://github.com/openziti/sdk-golang/issues/887) - Fix listener manager cleanup\n    * [Issue #886](https://github.com/openziti/sdk-golang/issues/886) - When controller is busy during service refresh, backoff and retry instead of falling back to full refresh\n    * [Issue #885](https://github.com/openziti/sdk-golang/issues/885) - Only compare relevant service fields when looking for changes\n    * [Issue #884](https://github.com/openziti/sdk-golang/issues/884) - Add deadline for bind establishment\n    * [Issue #883](https://github.com/openziti/sdk-golang/issues/883) - Router level listener can be left open if multi-listener closes during listener establishment\n    * [Issue #877](https://github.com/openziti/sdk-golang/issues/877) - Handle differences in xgress eof/end-of-circuit handling by adding a capabilities exchange\n    * [Issue #832](https://github.com/openziti/sdk-golang/issues/832) - Fuzz session refresh timers\n    * [Issue #879](https://github.com/openziti/sdk-golang/issues/879) - Return the connId in inspect response\n    * [Issue #878](https://github.com/openziti/sdk-golang/issues/878) - Fix responses from rx goroutines\n    * [Issue #874](https://github.com/openziti/sdk-golang/issues/874) - Add inspect support at the context level\n    * [Issue #871](https://github.com/openziti/sdk-golang/issues/871) - Make SDK better at sticking to MaxTerminator terminators\n    * [Issue #708](https://github.com/openziti/sdk-golang/issues/708) - Support for Go's built-in context in Dial methods\n    * [Issue #860](https://github.com/openziti/sdk-golang/issues/860) - Make the dialing identity's id and name available on dialed connections\n    * [Issue #857](https://github.com/openziti/sdk-golang/issues/857) - Use new error code and retry hints to correctly react to terminator errors\n    * [Issue #847](https://github.com/openziti/sdk-golang/issues/847) - Ensure the initial version check succeeds, to ensure we don't legacy sessions on ha or oidc-enabled controllers\n    * [Issue #824](https://github.com/openziti/sdk-golang/pull/824) - release notes and hard errors on no TOTP handler breaks partial auth events\n    * [Issue #818](https://github.com/openziti/sdk-golang/issues/818) - Full re-auth should not clear services list, as that breaks the on-change logic\n    * [Issue #817](https://github.com/openziti/sdk-golang/issues/817) - goroutines can get stuck when iterating over randomized HA controller list\n    * [Issue #736](https://github.com/openziti/sdk-golang/issues/736) - Migrate from github.com/mailru/easyjson\n    * [Issue #813](https://github.com/openziti/sdk-golang/issues/813) - SDK doesn't stop close listener when it detects that a service being hosted gets deleted\n    * [Issue #811](https://github.com/openziti/sdk-golang/issues/811) - Credentials are lost when explicitly set\n    * [Issue #807](https://github.com/openziti/sdk-golang/issues/807) - Don't send close from rxer to avoid blocking\n    * [Issue #800](https://github.com/openziti/sdk-golang/issues/800) - Tidy create service session logging\n\n* github.com/openziti/secretstream: [v0.1.39 -> v0.1.49](https://github.com/openziti/secretstream/compare/v0.1.39...v0.1.49)\n* github.com/openziti/storage: [v0.4.26 -> v0.4.39](https://github.com/openziti/storage/compare/v0.4.26...v0.4.39)\n    * [Issue #122](https://github.com/openziti/storage/issues/122) - StringFuncNode has incorrect nil check, allowing panic\n    * [Issue #120](https://github.com/openziti/storage/issues/120) - Change post tx commit constraint handling order\n    * [Issue #119](https://github.com/openziti/storage/issues/119) - Add ContextDecorator API\n\n* github.com/openziti/transport/v2: [v2.0.188 -> v2.0.215](https://github.com/openziti/transport/compare/v2.0.188...v2.0.215)\n    * [Issue #31](https://github.com/openziti/transport/issues/31) - ipv6 Transport Address Parsing\n    * [Issue #149](https://github.com/openziti/transport/issues/149) - Archive transwarp code\n\n* github.com/openziti/xweb/v3: [v2.3.4 -> v3.0.4](https://github.com/openziti/xweb/compare/v2.3.4...v3.0.4)\n    * [Issue #32](https://github.com/openziti/xweb/issues/32) - watched identities sometimes don't reload when changed\n\n* github.com/openziti/go-term-markdown: v1.0.1 (new)\n* github.com/openziti/ziti/v2: [v1.6.8 -> v2.0.0](https://github.com/openziti/ziti/compare/v1.6.8...v2.0.0)\n    * [Issue #3721](https://github.com/openziti/ziti/issues/3721) - Add CreateCircuitV3 to controller\n    * [Issue #3719](https://github.com/openziti/ziti/issues/3719) - Allow binding specific inspects to pass through to xgress listener implementations\n    * [Issue #3696](https://github.com/openziti/ziti/issues/3696) - oidc provider is non-deterministic for wildcard certs\n    * [Issue #3681](https://github.com/openziti/ziti/issues/3681) - coalesce OIDC JWT revocations to reduce controller write pressure\n    * [Issue #3683](https://github.com/openziti/ziti/issues/3683) - add fablab test for testing flow control changes over a longer term\n    * [Issue #3673](https://github.com/openziti/ziti/issues/3673) - revocation build-up in db and rdm\n    * [Issue #3674](https://github.com/openziti/ziti/issues/3674) - Update to Go 1.26\n    * [Issue #3496](https://github.com/openziti/ziti/issues/3496) - MFA TOTP Enrollment During OIDC Authentication Does Not Work\n    * [Issue #3609](https://github.com/openziti/ziti/issues/3609) - Stabilize terminator creation test for 2.0\n    * [Issue #3648](https://github.com/openziti/ziti/issues/3648) - tunnel: myCopy logs router ID as circuitId, causing misleading debug output\n    * [Issue #3658](https://github.com/openziti/ziti/issues/3658) - Raft cluster join fails with \"hello message too big\" when using long hostnames\n    * [Issue #3626](https://github.com/openziti/ziti/issues/3626) - controller: overlay bind point produces malformed URL in /versions apiBaseUrls\n    * [Issue #3635](https://github.com/openziti/ziti/issues/3635) - Allow controllers to dial routers to support more topologies\n    * [Issue #3607](https://github.com/openziti/ziti/issues/3607) - linux installer not upgradable from v1\n    * [Issue #3650](https://github.com/openziti/ziti/issues/3650) - Reroute doesn't proactively clean up orphaned route entries\n    * [Issue #3571](https://github.com/openziti/ziti/issues/3571) - Ensure 2.0 backwards compatibility with 1.6 and 1.5 using the smoketest\n    * [Issue #3636](https://github.com/openziti/ziti/issues/3636) - Adaptive rate limiter should use success rate rather than queue position\n    * [Issue #3600](https://github.com/openziti/ziti/issues/3600) - Add a preferredLeader flag, allow selected nodes to be preferred for raft leader, if they're available\n    * [Issue #3642](https://github.com/openziti/ziti/issues/3642) - Use xgress_common.Connection type for xgress_transport and xgress_proxy\n    * [Issue #3597](https://github.com/openziti/ziti/issues/3597) - Enable OIDC API by default\n    * [Issue #3624](https://github.com/openziti/ziti/issues/3624) - Multi-underlay control channel doesn't correctly handle lack of group secret on non-grouped underlays\n    * [Issue #3613](https://github.com/openziti/ziti/issues/3613) - Initializing cluster from existing db using `db:` config settings results in panic\n    * [Issue #3620](https://github.com/openziti/ziti/issues/3620) - Controller control channel heartbeats config parsing was erroneously nested under options parsing\n    * [Issue #3619](https://github.com/openziti/ziti/issues/3619) - Router connect events full sync interval was using min/max values meant for the batch interval\n    * [Issue #3618](https://github.com/openziti/ziti/issues/3618) - Router interfaceDiscovery.minReportInterval value was being set to checkInterval\n    * [Issue #3617](https://github.com/openziti/ziti/issues/3617) - Transit router disabled flag not passed through raft command structure\n    * [Issue #3333](https://github.com/openziti/ziti/issues/3333) - Updb user-lockout triggered by successful login attempts\n    * [Issue #3599](https://github.com/openziti/ziti/issues/3599) - Add gap detection and handling to router data model\n    * [Issue #3356](https://github.com/openziti/ziti/issues/3356) - Add WWW-Authenticate Headers\n    * [Issue #3074](https://github.com/openziti/ziti/issues/3074) - Dynamic cost range is too limited\n    * [Issue #3558](https://github.com/openziti/ziti/issues/3558) - terminator cost increased on egress dial success, not on circuit completion\n    * [Issue #3556](https://github.com/openziti/ziti/issues/3556) - global circuit costs not cleared when terminator is deleted\n    * [Issue #3557](https://github.com/openziti/ziti/issues/3557) - costing calculation for the weighted terminator selection strategy  is incorrect\n    * [Issue #2512](https://github.com/openziti/ziti/issues/2512) - Return circuit ID and error in dial failures\n    * [Issue #3569](https://github.com/openziti/ziti/issues/3569) - Version 2.0+ routers should not connect to controllers which do not support JWT formatted legacy sessions\n    * [Issue #3565](https://github.com/openziti/ziti/issues/3565) - Link dialer save 'is first conn' true, so all dials claim to be first, causing potential race condition\n    * [Issue #3550](https://github.com/openziti/ziti/issues/3550) - Support multi-underlay control channels\n    * [Issue #3535](https://github.com/openziti/ziti/issues/3535) - Remove the legacy xgress_edge_tunnel implementation\n    * [Issue #3547](https://github.com/openziti/ziti/issues/3547) - Add support for sending the dialing identity id and name to the hosting sdk\n    * [Issue #3541](https://github.com/openziti/ziti/issues/3541) - Remove option to disable the router data model in the controller\n    * [Issue #3540](https://github.com/openziti/ziti/issues/3540) - Handle UDP difference between proxy and tproxy implementations\n    * [Issue #3527](https://github.com/openziti/ziti/issues/3527) - ER/T UDP tunnels keep closed connections for 30s, preventing potential new good connections in that time\n    * [Issue #3526](https://github.com/openziti/ziti/issues/3526) - ER/T half-close logic is incorrect\n    * [Issue #3524](https://github.com/openziti/ziti/issues/3524) - Provide more error context to SDKs for terminator errors\n    * [Issue #3509](https://github.com/openziti/ziti/issues/3509) - Enforce policy on the router for oidc sessions, by closing open circuits and terminators when service access is lost\n    * [Issue #3531](https://github.com/openziti/ziti/issues/3531) - Remove created/updated/deleted terminator events. Obsoleted by entity change events.\n    * [Issue #3532](https://github.com/openziti/ziti/issues/3532) - Removed deprecated create identity <type> subcommands\n    * [Issue #3521](https://github.com/openziti/ziti/issues/3521) - Cleanup CLI to remove calls to os.Exit to be embed friendlier\n    * [Issue #3516](https://github.com/openziti/ziti/issues/3516) - Remove support for create terminator v1\n    * [Issue #3512](https://github.com/openziti/ziti/issues/3512) - Remove legacy link management code from the controller\n    * [Issue #3511](https://github.com/openziti/ziti/issues/3511) - router proxy mode fails to resolve interface if binding is 0.0.0.0\n    * [Issue #3503](https://github.com/openziti/ziti/issues/3503) - Allow routers to request current cluster membership information\n    * [Issue #3501](https://github.com/openziti/ziti/issues/3501) - Get cluster membership information from raft directly, rather than trying to cache it in the DB\n    * [Issue #3500](https://github.com/openziti/ziti/issues/3500) - Set a router data model timeline when initializing a new HA setup, rather than letting it stay blank\n    * [Issue #3504](https://github.com/openziti/ziti/issues/3504) - Reduce router data model full state updates\n    * [Issue #3492](https://github.com/openziti/ziti/pull/3492) - Bump openziti/ziti-console-assets from 3.12.9 to 4.0.0 in /dist/docker-images/ziti-controller in the all group\n    * [Issue #3484](https://github.com/openziti/ziti/issues/3484) - router ctrl channel handler for handling cluster changes has an initialization race condition\n    * [Issue #3477](https://github.com/openziti/ziti/issues/3477) - Optionally enable model changes triggered by login to be non-blocking and to be droppable if the system is under load\n    * [Issue #3473](https://github.com/openziti/ziti/issues/3473) - Enable tls handshake rate limiter by default and tweak default values.\n    * [Issue #3471](https://github.com/openziti/ziti/issues/3471) - Go tunneler is ignoring host config MaxConnections\n    * [Issue #3469](https://github.com/openziti/ziti/issues/3469) - Only send model updates on resubscribe if the RDM index has advanced\n    * [Issue #2573](https://github.com/openziti/ziti/issues/2573) - An edge router in a tight restart loop causes a resource leak on routers to which it connects.\n    * [Issue #3430](https://github.com/openziti/ziti/issues/3430) - Add permissions list to identity\n    * [Issue #2109](https://github.com/openziti/ziti/issues/2109) - Add Edge Management Read Only Capability\n    * [Issue #3435](https://github.com/openziti/ziti/issues/3435) - Add edge management API permissions by entity type and action\n    * [Issue #3441](https://github.com/openziti/ziti/issues/3441) - Update router connection tracker to interrogate active connections\n    * [Issue #3451](https://github.com/openziti/ziti/issues/3451) - ci - compare only stable releases when promoting\n    * [Issue #3437](https://github.com/openziti/ziti/issues/3437) - SDK OIDC token updates to routers should return an error if invalid\n    * [Issue #3348](https://github.com/openziti/ziti/issues/3348) - Unable to clear/reset the \"tags\" property on an entity to an empty object\n    * [Issue #3452](https://github.com/openziti/ziti/issues/3452) - `ziti agent cluster add` has bad behavior if the add address doesn't match the advertise address\n    * [Issue #3410](https://github.com/openziti/ziti/issues/3410) - Consolidate fabric REST API code with edge management and edge client code\n    * [Issue #3425](https://github.com/openziti/ziti/issues/3425) - RDM not properly responding to tunneler enabled flag changes\n    * [Issue #3420](https://github.com/openziti/ziti/issues/3420) - The terminator id cache uses the same id for all terminators in a host.v2 config, resulting in a single terminator\n    * [Issue #3419](https://github.com/openziti/ziti/issues/3419) - When using the router data model, precedence specified on the per-service identity mapping are incorrectly interpreted\n    * [Issue #3318](https://github.com/openziti/ziti/issues/3318) - Terminator creation seems to slow exponentially as the number of terminators rises from 10k to 20k to 40k\n    * [Issue #3407](https://github.com/openziti/ziti/issues/3407) - The CLI doesn't properly pass JWT authentication information to websocket endpoints\n    * [Issue #3359](https://github.com/openziti/ziti/issues/3359) - Ensure router data model subscriptions have reasonable performance and will scale\n    * [Issue #3354](https://github.com/openziti/ziti/issues/3354) - SDK/ENV Info from SDKs is not distributed in HA\n    * [Issue #3381](https://github.com/openziti/ziti/issues/3381) - the fabric service REST apis are missing the maxIdleTime property\n    * [Issue #3382](https://github.com/openziti/ziti/issues/3382) - Legacy service sessions generated pre-1.7.x are incompatible with v1.7.+ and need to be cleared\n    * [Issue #3339](https://github.com/openziti/ziti/issues/3339) - get router ctrl.endpoint from ctrls claim in JWT\n    * [Issue #3378](https://github.com/openziti/ziti/issues/3378) - login with file stopped working\n    * [Issue #3349](https://github.com/openziti/ziti/issues/3349) - UPDB OIDC login returns wrong content type\n    * [Issue #2324](https://github.com/openziti/ziti/issues/2324) - Add Ext-JWT/OIDC enrollment\n    * [Issue #3346](https://github.com/openziti/ziti/issues/3346) - Fix confusing attempt logging\n    * [Issue #3337](https://github.com/openziti/ziti/issues/3337) - Router reports \"no xgress edge forwarder for circuit\"\n    * [Issue #3345](https://github.com/openziti/ziti/issues/3345) - Clean up connect events tests and remove global XG registry\n    * [Issue #3264](https://github.com/openziti/ziti/issues/3264) - Allow routers to generate alert events in cases of service misconfiguration\n    * [Issue #3321](https://github.com/openziti/ziti/issues/3321) - Health Check API missing base path on discovery endpoint\n    * [Issue #3323](https://github.com/openziti/ziti/issues/3323) - router/tunnel static services fail to bind unless new param protocol is defined\n    * [Issue #3309](https://github.com/openziti/ziti/issues/3309) - Detect link connections meant for another router\n    * [Issue #3286](https://github.com/openziti/ziti/issues/3286) - edge-api binding doesn't have the correct path on discovery endpoints\n    * [Issue #3297](https://github.com/openziti/ziti/issues/3297) - stop promoting hotfixes downstream\n    * [Issue #3295](https://github.com/openziti/ziti/issues/3295) - make ziti tunnel service:port pairs optional\n    * [Issue #3291](https://github.com/openziti/ziti/issues/3291) - replace decommissioned bitnami/kubectl\n    * [Issue #3277](https://github.com/openziti/ziti/issues/3277) - Router can deadlock on closing a connection if the incoming data channel is full\n    * [Issue #3269](https://github.com/openziti/ziti/issues/3269) - Add host-interfaces config type\n    * [Issue #3258](https://github.com/openziti/ziti/issues/3258) - Add config type proxy.v1 so proxies can be defined dynamically for the ER/T\n    * [Issue #3259](https://github.com/openziti/ziti/issues/3259) - Interfaces config type not added due to wrong name\n    * [Issue #3265](https://github.com/openziti/ziti/issues/3265) - Forwarding errors should log at debug, since they are usual part of circuit teardown\n    * [Issue #3261](https://github.com/openziti/ziti/issues/3261) - ER/T dialed xgress connections may only half-close when peer is fully closed\n\n","mentions_count":1},{"url":"https://api.github.com/repos/openziti/ziti/releases/299675547","assets_url":"https://api.github.com/repos/openziti/ziti/releases/299675547/assets","upload_url":"https://uploads.github.com/repos/openziti/ziti/releases/299675547/assets{?name,label}","html_url":"https://github.com/openziti/ziti/releases/tag/v2.0.0-pre7","id":299675547,"author":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"node_id":"RE_kwDODVFMN84R3K-b","tag_name":"v2.0.0-pre7","target_commitish":"main","name":"v2.0.0-pre7","draft":false,"immutable":false,"prerelease":true,"created_at":"2026-03-21T03:44:56Z","updated_at":"2026-03-21T03:59:25Z","published_at":"2026-03-21T03:59:25Z","assets":[{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/378376849","id":378376849,"node_id":"RA_kwDODVFMN84WjZKR","name":"checksums.sha256.txt","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"text/plain; charset=utf-8","state":"uploaded","size":790,"digest":"sha256:f2a51876120e8097ad0b13b7b920ab3f87e38beb53b99565da2607dc897c2df2","download_count":3,"created_at":"2026-03-21T03:59:23Z","updated_at":"2026-03-21T03:59:23Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre7/checksums.sha256.txt"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/378376847","id":378376847,"node_id":"RA_kwDODVFMN84WjZKP","name":"sbom-v2.0.0-pre7.spdx.json","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/json","state":"uploaded","size":1009229,"digest":"sha256:a5e14e19a4561b1c4db55b3061a1997ca9d93760c8e7917062332adf60a47f0e","download_count":2,"created_at":"2026-03-21T03:59:23Z","updated_at":"2026-03-21T03:59:24Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre7/sbom-v2.0.0-pre7.spdx.json"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/378376850","id":378376850,"node_id":"RA_kwDODVFMN84WjZKS","name":"source-v2.0.0-pre7.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":3795202,"digest":"sha256:ebf2375179c67bdb1b08bbd587fe216163fa1c1b89e6279e28bdff1ff26e5af6","download_count":5,"created_at":"2026-03-21T03:59:23Z","updated_at":"2026-03-21T03:59:23Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre7/source-v2.0.0-pre7.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/378376830","id":378376830,"node_id":"RA_kwDODVFMN84WjZJ-","name":"ziti-darwin-amd64-2.0.0-pre7.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":53942716,"digest":"sha256:78973b91140d5b46413ee85f2867e97b26c4aa5a1c18f0801d13cf5418d42c60","download_count":11,"created_at":"2026-03-21T03:59:20Z","updated_at":"2026-03-21T03:59:23Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre7/ziti-darwin-amd64-2.0.0-pre7.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/378376844","id":378376844,"node_id":"RA_kwDODVFMN84WjZKM","name":"ziti-darwin-arm64-2.0.0-pre7.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":50302663,"digest":"sha256:009b23eba5719d0565a024ab8123ef940654d7d8b74a6b2dafa854337efe6fab","download_count":7,"created_at":"2026-03-21T03:59:22Z","updated_at":"2026-03-21T03:59:25Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre7/ziti-darwin-arm64-2.0.0-pre7.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/378376829","id":378376829,"node_id":"RA_kwDODVFMN84WjZJ9","name":"ziti-linux-amd64-2.0.0-pre7.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":55252103,"digest":"sha256:f89630b8d0c38e4794fb4d5309fd5e3dd0e1aefcd3a2298a00084f33ebabfc3d","download_count":123,"created_at":"2026-03-21T03:59:20Z","updated_at":"2026-03-21T03:59:23Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre7/ziti-linux-amd64-2.0.0-pre7.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/378376828","id":378376828,"node_id":"RA_kwDODVFMN84WjZJ8","name":"ziti-linux-arm-2.0.0-pre7.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":51746275,"digest":"sha256:5b875a5437b0049859e537d3f5997b3e6ac9707a6a8f69ee104376759f4e8dca","download_count":11,"created_at":"2026-03-21T03:59:20Z","updated_at":"2026-03-21T03:59:23Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre7/ziti-linux-arm-2.0.0-pre7.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/378376834","id":378376834,"node_id":"RA_kwDODVFMN84WjZKC","name":"ziti-linux-arm64-2.0.0-pre7.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":51761292,"digest":"sha256:04a4cf44b506608082fabae992b7f5c2a102ea40b78cdefc0fc3b143f2621ca2","download_count":47,"created_at":"2026-03-21T03:59:20Z","updated_at":"2026-03-21T03:59:23Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre7/ziti-linux-arm64-2.0.0-pre7.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/378376827","id":378376827,"node_id":"RA_kwDODVFMN84WjZJ7","name":"ziti-windows-amd64-2.0.0-pre7.zip","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/zip","state":"uploaded","size":44640632,"digest":"sha256:fe94de005c3544fe9c1df0d83dedcb9e8d95557bf74c7c80c3cc568f4fc9f046","download_count":12,"created_at":"2026-03-21T03:59:20Z","updated_at":"2026-03-21T03:59:22Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre7/ziti-windows-amd64-2.0.0-pre7.zip"}],"tarball_url":"https://api.github.com/repos/openziti/ziti/tarball/v2.0.0-pre7","zipball_url":"https://api.github.com/repos/openziti/ziti/zipball/v2.0.0-pre7","body":"# Release 2.0.0\n\n## What's New\n\nThis is the next major version release of OpenZiti, following the 1.0 release in April 2024.\nOf particular note is that HA controllers are now considered ready for general use.\nThis release also introduces a new permissions model, OIDC/JWT token-based enrollment, \nclustering performance improvements, and a number of other features and fixes. Because \nsome of these changes are not backwards compatible with older routers, we're marking this \nas a major version bump.\n\n### HA Controllers are now considered ready for general use\n\nThis is a pretty big milestone and marks the completion of work that's been ongoing for a couple of years.\nThe HA work has brought with it some notable changes. Authentication now uses JWTs by default. Using JWTs\nmeans the controllers don't need to store session and propagate them to the routers. This removes a bottleneck\nfrom the network and allows the load to be more easily distributed among controllers and routers.\n\nTo support distributed authentication, the routers now get a bespoke version of the data model. In addition\nto enabling distributed authentication this will allow us to remove the need for service polling and further \nreduce the load on controllers in the future.\n\n### Router Compatibility\n\nRelated to the JWT work, routers with version 2.+ will only work with controllers that are version 2.+. This means\nto upgrade your network, controllers should be upgraded first. Routers can then be upgraded individually.\n\n2.x routers should still work fine with older router versions.\n\nWe try very hard to avoid breaking changes like this, but sometimes the engineering trade-offs lead there. This change\nwas first made in the 1.7 release. That release has not been marked stable, and we have no plans to do so, because\nof the backwards incompatibility. \n\nIf you need to support in the 1.6.12+ range, see the section \"OIDC enabled by default\".\n\n### New Permissions Model (BETA)\n\nAs one feature goes out of beta, another arrives into beta. This release introduces a new permissions system\nfor more fine grained control to the management API. It's not expected to change, but may do so based on feedback\nfrom users.\n\n### Updated Release Process\n\nWe have moved to creating `-preN` releases for major and minor versions. This way we can put out release candidates,\nor feature previews and put them through internal testing and let interested folks from the community try them out.\nThen, when we're ready, we can run the full validation suite against the last pre-release and retag it. \n\nPatch releases won't have `-preN` and should contain only high priority bug fixes.\n\n### Deprecation Cleanup\n\nSince we already have a breaking change, we're removing some other backwards compatibility code.\n\n* Controller managed links \n    * Router managed links were introduced in v0.30.0. \n    * If you're upgrading from an older versions, you'll want to upgrade to the latest 1.x release before jumping to 2.x\n    * Github tracking issue: https://github.com/openziti/ziti/issues/3512\n* `ziti edge create identity <type>`\n    * Identity types other than router were removed in v0.30.2\n    * The `type` can be dropped from the CLI command\n    * Github tracking issue: https://github.com/openziti/ziti/issues/3532\n* Terminator create/update/delete events\n    * These have been superseded by entity change events, which also have create/update/delete events for terminators\n    * Entity change events were introduced in v0.28.0\n    * Github tracking issue: https://github.com/openziti/ziti/issues/3531\n* `xgress_edge_tunnel` v1\n    * This is the first implementation of the tunneler in edge-router code (ER/T) which used legacy api sessions and services\n    * The v2 version uses the router data model and was introduced in v0.30.x\n    * Github tracking issue: https://github.com/openziti/ziti/issues/3516\n\n### Legacy Session Deprecation\n\nOIDC sessions are now preferred. They are the default, or will become the default for SDKs and tunnelers. They are also required\nwhen running HA. Legacy API and service session are now deprecated and will be removed in the OpenZiti v3.0.0 release. \n\n### Additional Features\n\n* Controllers can now optionally bind APIs using an OpenZiti identity\n* `ziti edge login` now supports the `--network-identity` flag to authenticate and establish connections through the Ziti overlay network\n* `ziti edge login` now supports using a bearer token with `--token` for authentication. The token is expected to be\n  provided as just the JWT, not with the \"Bearer \" prefix\n* Identity configuration can now be loaded from files or environment variables for flexible deployment scenarios\n* Identities can now be provisioned just-in-time through OIDC/JWT token-based enrollment\n* Multiple model updates can now be in-flight at the same time, improving clustering performance\n* Authentication-related model updates can now be non-blocking and even dropped if the system is too busy\n* Routers now provide more error context to SDKs for terminator errors, enabling better retry behavior\n* New `proxy.v1` config type for dynamic service proxies (originally released in 1.7.0)\n* New alert event type for surfacing operational issues to network operators - Beta (originally released in 1.7.0)\n* New Azure Service Bus event sink for streaming controller events, contributed by @ffaraone (originally released in 1.7.0)\n* Bundled ZAC upgraded to 4.0\n* Build updated to Go 1.25\n* CLI cleaned up to remove calls to `os.Exit`, making it more friendly for embedding\n* OIDC is now enabled by default\n* Controller Edge APIs now return `WWW-Authenticate` response headers on `401 Unauthorized` responses, giving clients actionable information about which auth methods are accepted and what went wrong\n* HA Controllers can be marked as 'preferredLeader' via config\n* Dynamic cost range for smart routing expanded beyond the previous 64K limit\n* Dial failures now return the circuit ID and error information for easier debugging\n* Router-to-controller control channels now support multiple underlays with priority-based message routing\n* The dialing identity's ID and name are now forwarded to the hosting SDK\n* Controllers can now dial routers to establish control channels, enabling connectivity when routers are behind firewalls (Beta)\n* Refresh-token revocations are now batched and best-effort, removing the database/raft bottleneck on token refreshes\n\n## Basic Permission System (BETA)\n\nAdded a basic permission system that allows more control over identity access to controller management API operations. \nThis replaces the previous binary admin/non-admin model with a more flexible permission system.\n\n**NOTE:** This feature is in BETA, primarily so we can get feedback on which permissions make sense. The implementation is unlikely to change\nbut the set of exposed permissions may grow, shrink or change based on user feedback. \n\n### Permission Model\n\nThe permission system supports three levels of authorization:\n\n  1. **Global Permissions**: System-wide access levels\n     - `admin` - Full access to all operations. This is still controlled by the `isAdmin` flag on identity\n     - `admin_readonly` - Read-only access to all resources except debugging facilities inspect and validate\n\n  2. **Entity-Level Permissions**: Full CRUD access to specific entity types\n     - Granting an entity-level permission (e.g., `service`) provides complete create, read, update, and delete access for that entity type\n\n  3. **Action-Level Permissions**: Specific operation access on entity types\n     - Fine-grained control using the pattern `<entity>.<action>` (e.g., `service.read`, `identity.update`)\n     - Supports `create`, `read`, `update`, and `delete` actions per entity type\n\n### Supported Entity Permissions\n\nThe following entity-level permissions are available:\n\n- `auth-policy` - Authentication policy management\n- `ca` - Certificate Authority management\n- `config` - Configuration management\n- `config-type` - Configuration type management\n- `edge-router-policy` - Edge router policy management\n- `enrollment` - Enrollment management\n- `external-jwt-signer` - External JWT signer management\n- `identity` - Identity management\n- `posture-check` - Posture check management\n- `router` - Edge and transit router management\n- `service` - Service management\n- `service-policy` - Service policy management\n- `service-edge-router-policy` - Service edge router policy management\n- `terminator` - Terminator management\n- `ops` - Operational resources (API sessions, sessions, circuits, links, inspect and validate)\n\n### Permission Assignment\n\nPermissions are assigned to identities via the `permissions` field in the identity resource. Multiple permissions can be granted to a single identity, and permissions are additive.\n\n### Cross-Entity Operations\n\nListing related entities through an entity's endpoints requires appropriate permissions for the related entity type. For example:\n- Listing services for a service-policy requires `service.read` permission\n- Listing identities for an edge-router-policy requires `identity.read` permission\n- Listing configs for a service requires `config.read` permission\n\n**NOTE:** \nMore permissions than expected may be required when performing actions through the CLI or ZAC. Take for example, when an identity \nhas `config.create` and is attempting to create a new config. The CLI may fail if the identity doesn't have `config-type.read`\nas well because it will need to look up the config type id that corresponds to the given config type name.\n\nSimilar cross entity read permissions may be required when creating services.\n\n### Admin Protection\n\nNon-admin identities cannot:\n- Create identities with the `isAdmin` flag\n- Create identities with any permissions granted\n- Modify admin-related fields on existing identities\n- Update or delete admin identities\n- Grant permissions to identities\n\nThese protections ensure that privilege escalation is prevented and admin access remains controlled.\n\n\n## Binding Controller APIs With Identity\n\nController APIs can now be bound to an OpenZiti overlay network identity, allowing secure communication through\nthe Ziti network. This is useful for scenarios where you want to expose controller APIs only through the overlay\nnetwork rather than on a standard network interface.\n\n### Configuration Structure\n\nA standard `bindPoint` configuration looks like this:\n```text\n    bindPoints:\n      - interface: 127.0.0.1:18441\n        address: 127.0.0.1:18441\n```\n\nTo bind controller APIs to an OpenZiti identity, add an additional `identity` block to your `bindPoints`. The\nidentity configuration specifies where to load the Ziti identity file and which service to bind it to:\n\n```text\n    bindPoints:\n      - interface: 127.0.0.1:18441\n        address: 127.0.0.1:18441\n      - identity:\n          file: \"c:/temp/ctrl.testing/ctrl.identity.json\"\n          service: \"mgmt\"\n```\n\n### Supported Configuration Options\n\n- `file`: Path to a Ziti identity JSON file containing the controller's identity and enrollment certificate\n- `env`: Name of an environment variable containing a base64-encoded Ziti identity (alternative to `file`)\n- `service`: The name of the Ziti service to bind the controller API to\n\n### Using Environment Variables\n\nFor deployments where storing identity files on disk is not preferred, you can reference a base64-encoded\nidentity file from an environment variable. The environment variable should contain the base64-encoded contents\nof the identity JSON file.\n\nFor example, if an environment variable named `ZITI_CTRL_IDENTITY` contains a base64-encoded identity file:\n\n```text\n    bindPoints:\n      - interface: 127.0.0.1:18441\n        address: 127.0.0.1:18441\n      - identity:\n          env: ZITI_CTRL_IDENTITY\n          service: \"mgmt\"\n```\n\n### IPv6 Support\n\nBoth IPv4 and IPv6 addresses are supported for standard bind points. IPv6 addresses should be specified in bracket\nnotation with a port number:\n\n```text\n    bindPoints:\n      - interface: \"[::1]:18441\"\n        address: \"[::1]:18441\"\n      - identity:\n          file: \"/path/to/identity.json\"\n          service: \"mgmt\"\n```\n\n## CLI Enhancements for Identity-Based Connections\n\nThe `ziti edge login` command and REST client utilities have been enhanced to support identity-based connections\nthrough the Ziti overlay network.\n\n### New `--network-identity` Flag for `ziti edge login`\n\nThe `ziti edge login` command now includes a `--network-identity` flag that allows you to authenticate to a Ziti\ncontroller through the overlay network using a Ziti identity:\n\n```bash\nziti edge login https://ziti.mgmt.apis.local:1280 \\\n  --username myuser \\\n  --password mypass \\\n  --network-identity /path/to/identity.json\n```\n\nThis is useful when the controller is only accessible through the Ziti overlay network or when you want to ensure\nall communication to the controller flows through the overlay for security purposes.\n\n### Identity Resolution Order\n\nWhen establishing connections, identities are resolved in the following order:\n\n1. **Command-line flag**: The `--network-identity` flag takes precedence\n2. **Environment variable**: If `ZITI_CLI_NETWORK_ID` is set and contains a base64-encoded identity, it is used\n3. **Cached identity file**: If a network identity was saved from a previous login in the Ziti config directory, it may be used\n\nThis layered approach allows for flexibility in deployment scenarios:\n- Development: Use command-line flags for quick testing\n- Automation: Use environment variables in CI/CD pipelines\n- Production: Cache identities securely for repeated access\n\n#### Dialing Modes When Authenticating\n\nThe CLI supports two dialing modes:\n\n**Intercept-based Dialing (Default)**\nBy default, URLs are expected to leverage intercepts. Create a service with an appropriate intercept config and use\nthe intercept address when dialing. This is the standard mode for most use cases. For example, given a service with\nthe intercept `ziti.mgmt.apis.local`\n```bash\nziti edge login https://ziti.mgmt.apis.local:1280 \\\n  --username myuser \\\n  --password mypass \\\n  --network-identity /path/to/identity.json\n```\n\n**Identity-aware Dialing (Addressable Terminators)**\nTo support addressable terminators-based dialing, specify a user in the URL. This activates dial-by-identity\nfunctionality. The URL format should be `identity-to-dial@service-name-to-dial`. For example:\n```bash\nziti edge login https://my-identity@my-service:1280 \\\n  --username myuser \\\n  --password mypass \\\n  --network-identity /path/to/identity.json\n```\n\nIn this mode, the transport extracts the identity from the URL and uses it to establish a direct connection to\nthe specified service via the addressable terminator.\n\n\n## OIDC/JWT Token-based Enrollment\n\nOpenZiti now supports provisioning identities just-in-time through OIDC/JWT token enrollment. External identity \nproviders can be configured to allow identities to enroll using JWT tokens, with support for the resulting \nidentities to use certificate or token authentication.\n\n### External JWT Signer Configuration\n\nExternal JWT signers are configured via the Edge Management API to define enrollment behavior with the following new \nenrollment-specific properties:\n\n- **enrollToCertEnabled** - When enabled, identities can exchange a JWT token and a certificate signing request (CSR) \n        for a client certificate during enrollment. The certificate can then be used for standard certificate-based\n        authentication.\n\n- **enrollToTokenEnabled** - When enabled, identities can use a JWT token to enroll. The current token or future tokens\n        may be used for authentication.\n\n- **enrollNameClaimsSelector** - Specifies which JWT claim contains the identity name. Accepts a JSON pointer \n        (e.g., `/preferred_username`) or a simple property name (e.g., `preferred_username`, automatically converted to\n        `/preferred_username`). Defaults to `/sub` if not specified. The extracted value becomes the identity name in Ziti.\n\n- **enrollAttributeClaimsSelector** - Specifies which JWT claims to extract as identity attributes during enrollment.\n        Accepts a JSON pointer (e.g., `/roles`) or a simple property name (e.g., `roles`). Extracted attributes are \n        applied to the newly enrolled identity for use in authorization policies.\n\n- **enrollAuthPolicyId** - Specifies the authentication policy to apply to newly enrolled identities. This determines\n        what authentication methods are available for the identity post-enrollment.\n\nAdditionally the existing property named **claimsProperty** that specifies external id to match identities to:\n\n- now supports a JSON pointer (e.g., `/id`) or a simple property name (e.g., `id`)\n- is used to populate the `externalId` field of the identity\n\n### Enrollment Paths\n\n#### Certificate Enrollment (enrollToCertEnabled)\n\nWhen certificate enrollment is enabled, unauthenticated users can:\n\n1. Obtain a list of available IdPs from the public Edge Client API `GET /external-jwt-signers` endpoint, where \n   `enrollToCertEnabled` is set to `true`\n2. Obtain a JWT from the configured OIDC provider\n3. Generate a certificate signing request (CSR)\n4. Submit an enrollment request with the JWT and CSR\n5. Have their identity created in Ziti with attributes extracted from JWT claims\n6. Receive a signed client certificate for certificate-based authentication\n\n#### Token Enrollment (enrollToTokenEnabled)\n\nWhen token enrollment is enabled, unauthenticated users can:\n\n1. Obtain a list of available IdPs from the public Edge Client API `GET /external-jwt-signers` endpoint, where \n   `enrollToTokenEnabled` is set to `true`\n1. Obtain a JWT from the configured OIDC provider\n2. Submit an enrollment request with the JWT\n3. Have their identity created in Ziti with attributes extracted from JWT claims\n4. Receive a Ziti API token for token-based authentication\n\n### Edge Management API\n\nThe Edge Management API provides full CRUD operations for configuring external JWT signers:\n\n- `POST /external-jwt-signers` - Create a new external JWT signer with all configuration options\n- `GET /external-jwt-signers` - List all configured external JWT signers\n- `GET /external-jwt-signers/{id}` - Retrieve a specific signer configuration\n- `PUT /external-jwt-signers/{id}` - Update all fields of a signer\n- `PATCH /external-jwt-signers/{id}` - Partially update a signer\n- `DELETE /external-jwt-signers/{id}` - Delete a signer\n\n### Edge Client API\n\nThe Edge Client API exposes a reduced set of external JWT signer information for unauthenticated enrollment requests:\n\n- `GET /external-jwt-signers` - List available JWT signers with enrollment capabilities\n\nThe client API response includes the following fields for each signer:\n\n- `name` - Signer name\n- `externalAuthUrl` - URL where users obtain JWT tokens\n- `clientId` - OIDC client ID\n- `scopes` - Requested OIDC scopes\n- `openIdConfigurationUrl` - OIDC discovery endpoint\n- `audience` - Expected token audience\n- `targetToken` - Token type to use (ACCESS or ID)\n- **`enrollToCertEnabled`** - Flag indicating certificate enrollment is available\n- **`enrollToTokenEnabled`** - Flag indicating token enrollment is available\n\n### CLI Commands\n\n**Create an external JWT signer with enrollment options:**\n```\nziti edge controller create ext-jwt-signer <name> <issuer> \\\n  --jwks-endpoint <url> \\\n  --audience <audience> \\\n  --enroll-to-cert \\\n  --enroll-to-token=false \\\n  --enroll-name-claims-selector preferred_username \\\n  --enroll-attr-claims-selector roles \\\n  --enroll-auth-policy <policy-id-or-name>\n```\n\n**Update enrollment options on an existing signer:**\n```\nziti edge controller update ext-jwt-signer <name|id> \\\n  --enroll-to-cert \\\n  --enroll-auth-policy <policy-id-or-name>\n```\n\n**List external JWT signers:**\n```\nziti edge controller list ext-jwt-signers\n```\n\n## Clustering Performance Improvements\n\nIn previous releases, model updates were submitted to raft one at at time. This prevented \nraft from being efficient by allowing command batching. This release allows multiple \nmodel updates to be in-flight at the same time. \n\nNew Configuration Options\n\n1. Raft Apply Timeout (cluster.applyTimeout)\n\nLocation: Controller configuration file, under the cluster section\nType: Duration\nDefault: 5s\nDescription: Timeout for applying commands to the Raft distributed log. Commands that exceed this timeout will trigger adaptive rate limiter backoff.\n\nExample:\n```\ncluster:\n  applyTimeout: 10s\n```\n\n2. Cluster Rate Limiter Configuration (cluster.rateLimiter)\n\nA new adaptive rate limiter that controls the submission of commands to the Raft cluster. Unlike the existing command rate limiter, this specifically manages in-flight Raft operations with adaptive window sizing.\n\nConfiguration Structure:\n```\ncluster:\n  rateLimiter:\n    enabled: true\n    minSize: 5\n    maxSize: 250\n    timeout: 30s\n```\n\nSub-options:\n\n  - enabled (boolean)\n    - Default: true\n    - Description: Enable/disable adaptive rate limiting for Raft command submission\n  - minSize (integer)\n    - Default: 5\n    - Minimum: 1\n    - Description: Minimum window size for concurrent in-flight Raft operations\n  - maxSize (integer)\n    - Default: 250\n    - Description: Maximum window size for concurrent in-flight Raft operations. Must be >= minSize\n  - timeout (duration)\n    - Default: 30s\n    - Description: Time after which outstanding work is assumed to have failed if not marked completed\n\n3. Restart Self on Snapshot (cluster.restartSelfOnSnapshot)\n\nLocation: Controller configuration file, under cluster section\nType: Boolean\nDefault: false\nDescription: When true, the controller will automatically restart itself when restoring a snapshot to an initialized system. When false, the controller will exit with code 0, requiring external process management to restart it.\n\nExample:\n```\ncluster:\n    restartSelfOnSnapshot: true\n```\n\n### New Metrics\n\nThe adaptive rate limiter exposes three new metrics:\n\n  1. raft.rate_limiter.queue_size (gauge)\n    - Current number of operations queued/in-flight\n  2. raft.rate_limiter.work_timer (timer)\n    - Duration of rate-limited operations\n  3. raft.rate_limiter.window_size (gauge)\n    - Current adaptive window size\n\n## Rate Limiter Algorithm Improvements\n\nThe adaptive rate limiter tracker now uses a success rate metric to decide when to grow or shrink its\nconcurrency window, instead of using raw queue position. An exponentially decaying histogram tracks the\nratio of successes to backoffs. When the success rate exceeds a configurable threshold, the window is\ngrown by a multiplicative increase factor. When it falls below the threshold, the window is shrunk by a\nmultiplicative decrease factor. The check intervals for increase and decrease are independently configurable.\n\nThis approach produces smoother, more stable window adjustments under varying load, avoiding the\nover-aggressive shrinking that could occur when queue position alone was used as the signal.\n\nThis specific rate limiter implementation is used in three places:\n* **Controller TLS handshake rate limiting** - controls the rate of incoming TLS handshakes on the controller\n* **Raft command submission** - controls the rate of commands submitted to the Raft distributed log\n* **Router control channel rate limiting** - controls the rate of requests from the router to the controller\n\nNote: this work was done in advance of enabling the TLS handshake rate limiter by default. The TLS\nhandshake rate limiter is not yet enabled by default, but can be enabled via the `tls.rateLimiter`\nconfiguration section.\n\nNew configuration options (available under each `rateLimiter` section):\n\n  - successThreshold (float)\n    - Default: 0.9\n    - Description: Success rate threshold above which the window size will be increased and below which it will be decreased\n  - increaseFactor (float)\n    - Default: 1.02\n    - Description: Multiplier applied to the current window size when growing. Must be greater than 1\n  - decreaseFactor (float)\n    - Default: 0.9\n    - Description: Multiplier applied to the current window size when shrinking. Must be between 0 and 1\n  - increaseCheckInterval (integer)\n    - Default: 10\n    - Description: Number of successes between window size increase checks\n  - decreaseCheckInterval (integer)\n    - Default: 10\n    - Description: Number of backoffs between window size decrease checks\n\n## Background Processing for Identity Updates\n\nIdentity environment and authenticator updates that occur during authentication are now processed asynchronously in the background. \nThis prevents authentication requests from blocking when the system is under load, significantly improving resilience during thundering herd scenarios.\n\nWhen the background queue fills up, updates can be dropped as they will be refreshed on the next authentication attempt. \nThis allows the system to gracefully handle load spikes without impacting authentication performance.\n\nFor now, dropping entries when the queue fills will be disabled by default, but can be enabled, see below.\n\n### Configuration\n\nA new `command.background` configuration section controls the background processing behavior:\n\n```yaml\n  command:\n    background:\n      enabled: true           # Enable background processing (default: true)\n      queueSize: 1000        # Maximum queue size (default: 1000)\n      dropWhenFull: true     # Drop updates when queue is full (default: false)\n      delayThreshold: 50ms   # The threshold for how long updates are taking before starting to background updates\n```\n\nNote that the `commandRateLimiter` configuration section may instead be specified under `command` as `rateLimiter`.\n\nExample:\n\n```yaml\n  command:\n    background:\n      enabled: true\n      queueSize: 250\n      dropWhenFull: false\n      delayThreshold: 50ms\n    rateLimiter:\n      enabled:   true\n      maxQueued: 25\n```\n\nNote that if command rate limiter configuration is specified in both locations, the settings under `command` will take \nprecedence. The standalone `commandRateLimiter` section may be deprecated in the future.\n\n### Metrics\n\nWhen background processing is enabled, the following metrics are exposed:\n\n- command.background.queue_size - Current number of queued background tasks\n- command.background.worker_count - Current number of worker goroutines\n- command.background.busy_workers - Number of workers currently processing tasks\n- command.background.work_timer - Timer tracking background task execution (includes histogram, meter, and count)\n- command.background.dropped_entries - Count of dropped updates when queue is full (only when dropWhenFull is enabled)\n\n## New proxy.v1 Config Type\n\n*Originally released in 1.7.0*\n\nAdded support for dynamic service proxies with configurable binding and protocol options.\nThis allows Edge Routers and Tunnelers to create proxy endpoints that can forward traffic for Ziti services.\n\nThis differs from intercept.v1 in that intercept.v1 will intercept traffic on specified\nIP addresses or DNS entries to forward to a service using tproxy or tun interface,\ndepending on implementation.\n\nA proxy on the other hand will just start a regular TCP/UDP listener on the configured port,\nso traffic will have to be configured for that destination.\n\nExample proxy.v1 Configuration:\n\n```\n  {\n    \"port\": 8080,\n    \"protocols\": [\"tcp\"],\n    \"binding\": \"0.0.0.0\"\n  }\n```\n\nConfiguration Properties:\n  - port (required): Port number to listen on (1-65535)\n  - protocols (required): Array of supported protocols (tcp, udp)\n  - binding (optional): Interface to bind to. For the ER/T defaults to the configured lanIF config property.\n\nThis config type is currently supported by the ER/T when running in either proxy or tproxy mode.\n\n## Alert Events (BETA)\n\n*Originally released in 1.7.0*\n\nA new alert event type has been added to allow Ziti components to emit alerts for issues that network operators can address.\nAlert events are generated when components encounter problems such as service configuration errors or resource\navailability issues.\n\nAlert events include:\n  - Alert source type and ID (currently supports routers, with controller and SDK support planned for future releases)\n  - Severity level (currently supports error, with info and warning planned for future releases)\n  - Alert message and supporting details\n  - Related entities (router, identity, service, etc.) associated with the alert\n\nExample alert event when a router cannot bind a configured network interface:\n\n```\n  {\n    \"namespace\": \"alert\",\n    \"event_src_id\": \"ctrl1\",\n    \"timestamp\": \"2021-11-08T14:45:45.785561479-05:00\",\n    \"alert_source_type\": \"router\",\n    \"alert_source_id\": \"DJFljCCoLs\",\n    \"severity\": \"error\",\n    \"message\": \"error starting proxy listener for service 'test'\",\n    \"details\": [\n      \"unable to bind eth0, no address\"\n    ],\n    \"related_entities\": {\n      \"router\": \"DJFljCCoLs\",\n      \"identity\": \"DJFljCCoLs\",\n      \"service\": \"3DPjxybDvXlo878CB0X2Zs\"\n    }\n  }\n```\n\nAlert events can be consumed through the standard event system and logged to configured event handlers for monitoring and alerting purposes.\n\nThese events are currently in Beta, as the format is still subject to change. Once they've been in use in production for a while\nand proven useful, they will be marked as stable.\n\n## Azure Service Bus Event Sink\n\n*Originally released in 1.7.0. Contributed by @ffaraone.*\n\nAdds support for streaming controller events to Azure Service Bus.\nThe new logger enables real-time event streaming from the OpenZiti controller to Azure Service Bus\nqueues or topics, providing integration with Azure-based monitoring and analytics systems.\n\nTo enable the Azure Service Bus event logger, add configuration to the controller config file under the events section:\n\n```\n  events:\n    serviceBusLogger:\n      subscriptions:\n        - type: circuit\n        - type: session\n        - type: metrics\n          sourceFilter: .*\n          metricFilter: .*\n        # Add other event types as needed\n      handler:\n        type: servicebus\n        format: json\n        connectionString: \"Endpoint=sb://your-namespace.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=your-key\"\n        topic: \"ziti-events\"          # Use 'topic' for Service Bus topic\n        # queue: \"ziti-events-queue\"  # Or use 'queue' for Service Bus queue\n        bufferSize: 100                # Optional, defaults to 50\n```\n\n- Required configuration:\n    - format: Event format, currently supports only json\n    - connectionString: Azure Service Bus connection string\n    - Either topic or queue: Destination name (mutually exclusive)\n\n- Optional configuration:\n    - bufferSize: Internal message buffer size (default: 50)\n\n## OIDC is now enabled by default\n\nThe controller now automatically adds the `edge-oidc` API binding to any web listener that hosts\nthe `edge-client` API, even when `edge-oidc` is not explicitly listed in the `web` section of the\nconfiguration. This means OIDC-based authentication is available out of the box without requiring\nchanges to existing controller configurations.\n\n### Where OIDC binds\n\nThe `edge-oidc` binding is added to the same web listener(s) as `edge-client`. If `edge-client` is\nhosted on `127.0.0.1:1280`, the OIDC endpoints will be available on that same address under the\n`/oidc` path (e.g. `https://127.0.0.1:1280/oidc/.well-known/openid-configuration`).\n\nThe controller capabilities returned by `GET /version` will include `OIDC_AUTH` and the\n`edge-oidc` entry will be present in `apiVersions` when OIDC is active.\n\n### Opting out\n\nIf OIDC should not be enabled automatically, set `disableOidcAutoBinding: true` under `edge.api`:\n\n```yaml\nedge:\n  api:\n    address: 127.0.0.1:1280\n    sessionTimeout: 30m\n    disableOidcAutoBinding: true\n```\n\nWhen `disableOidcAutoBinding` is `true`, OIDC will only be active if `edge-oidc` is explicitly\nlisted as a binding in the `web` section. \n\nWhen OIDC is not enabled, all (SDK) clients will revert to using legacy authentication.\nLegacy authentication does not support multiple controllers or HA in general. Clients will treat\ntheir controller as if it is a single controller instance and will function as if no other controllers\nexist.\n\n\n## WWW-Authenticate Headers\n\nThe controller's Edge APIs now include `WWW-Authenticate` headers on `401 Unauthorized` responses,\nper issuer: https://github.com/openziti/ziti/issues/3356. These headers provide insight into why\na token was rejected. The main benefit of these headers is to convey information for JWT backed\nAPI Sessions and API Session authentication where a token may not have been provided (missing),\nis used beyond its expiration date (expired), or the token has become invalid for any other\nreason (invalid).\n\n`www-authenticate` headers are provided as a single header instance with multiple challenge values\nseparate by commas.\n\n### No Credentials Provided\n\nWhen a request hits a protected endpoint without any credentials, a single `WWW-Authenticate` header\nis returned listing both accepted auth schemes as comma-separated challenges:\n\n```\nWWW-Authenticate: zt-session realm=\"zt-session\" error=\"missing\" error_description=\"no matching token was provided\",Bearer realm=\"openziti-oidc\" error=\"missing\" error_description=\"no matching token was provided\"\n```\n\n### Token Errors\n\nWhen a token is present but cannot be accepted, the header identifies the scheme and what went wrong:\n\n```\nWWW-Authenticate: Bearer realm=\"openziti-oidc\" error=\"expired\" error_description=\"token expired\"\nWWW-Authenticate: Bearer realm=\"openziti-oidc\" error=\"invalid\" error_description=\"token is invalid\"\nWWW-Authenticate: zt-session realm=\"zt-session\" error=\"invalid\" error_description=\"token is invalid\"\n```\n\n### OIDC External JWT — Primary Authentication\n\nWhen an auth policy requires an external JWT signer for primary authentication (e.g., a PKCE flow\nbacked by an ext-jwt signer), the header identifies which signers are accepted and what went wrong.\nMultiple accepted signers are pipe-delimited in the `id` and `issuer` parameters:\n\n```\nWWW-Authenticate: Bearer realm=\"openziti-primary-ext-jwt\" error=\"missing\" error_description=\"no matching token was provided\" id=\"signer-id-1|signer-id-2\" issuer=\"https://issuer1.example.com|https://issuer2.example.com\"\n```\n\nThe `error` value follows the same `missing`/`expired`/`invalid` pattern as standard bearer token errors.\n\n### OIDC External JWT — Secondary / MFA Authentication\n\nWhen an auth policy requires an external JWT signer as a secondary factor (step-up after primary\nauth succeeds), the header identifies the single required signer. The `error` value follows the\nsame `missing`/`expired`/`invalid` pattern:\n\n```\nWWW-Authenticate: Bearer realm=\"openziti-secondary-ext-jwt\" error=\"missing\" error_description=\"no matching token was provided\" id=\"<signer-id>\" issuer=\"<issuer>\"\n```\n\n### Anonymous Endpoints\n\nUnauthenticated endpoints such as version information do not return `WWW-Authenticate` headers.\n\n## HA Preferred Leaders\n\nControllers can be marked as a preferred leader. \n\n**Example Config**\n```yaml\ncluster:\n  dataDir: /home/{{ .Model.MustVariable \"credentials.ssh.username\" }}/fablab/ctrldata\n  preferredLeader: true\n```\n\nIf a controller that is not marked preferredLeader becomes a preferredLeader, it will check \nif there's a node available that is marked as preferred. If there is one, or one later\njoins the cluster, the non-preferred node will attempt to transfer leadership to the \nnode that is marked as preferred.\n\n## Expanded Dynamic Cost Range\n\nThe dynamic cost range for smart routing has been expanded beyond the previous 64K limit. Under high\nload, terminators could saturate the cost space, making dynamic cost values meaningless for routing\ndecisions and leading to uneven load distribution. The expanded range allows for more granular cost\ndifferentiation even under heavy load.\n\n## Circuit ID and Error in Dial Failures\n\nDial failures now return the circuit ID and, when available, the error that caused the circuit to fail.\nPreviously, the circuit ID was only returned on successful dials. Note that SDKs will need to be\nupdated to surface the circuit id when a dial failure happens.\n\n## Multi-Underlay Control Channels\n\nRouter-to-controller control channels now support multiple underlays with priority-based message routing.\nThis allows time-sensitive control messages (heartbeats, routing, circuit requests) to be separated from\noperational data (metrics, inspections) across dedicated TCP connections, preventing bulk operations from\ndelaying user-affecting control plane traffic. This feature does not yet allow specifying multiple \nnetwork interfaces to use, to load balance data across.\n\n## Dialing Identity Forwarded to Hosting SDK\n\nThe identity ID and name of the dialing client are now forwarded to the hosting SDK when a circuit is\nestablished. This allows hosting applications to identify which identity initiated the connection,\nenabling identity-aware request handling on the server side. This will require SDK updates to add this\nto the API for hosting applications.\n\n## Controller-Initiated Control Channel Dials (BETA)\n\nControllers can now dial routers to establish control channels. Previously, routers were solely\nresponsible for dialing controllers. This feature is designed for deployments where one or more\ncontrollers are in a private network that routers cannot reach, but the controllers can dial out.\nA common scenario is an HA cluster where some controllers are publicly reachable and some are in\na private network. External routers connect to the public controllers normally, and the private\ncontrollers dial out to the routers.\n\n### Router Configuration\n\nRouters can configure one or more control channel listeners. Each listener specifies a bind address,\nan advertise address (reported to the controller), and optional groups for matching.\n\n```yaml\nctrl:\n  listeners:\n    - bind: tls://0.0.0.0:6262\n      advertise: tls://router.example.com:6262\n      groups:\n        - default\n```\n\nThe router is the authoritative source of ctrl channel listener information, similar to link\nlisteners. When a router connects to any controller, it reports its configured `ctrlChanListeners`\nand the controller data model is updated automatically. This means that in most deployments —\nwhere the router can reach at least one controller — no manual configuration of listener addresses\nis needed on the controller side.\n\nThe `ctrlChanListeners` field can also be set via the CLI for cases where a router cannot reach\nany controller and thus cannot initialize the data model itself:\n\n```bash\nziti edge update edge-router myRouter --bootstrap-ctrl-chan-listener 'tls://router.example.com:6262=group1,group2'\n```\n\nGroups default to `[\"default\"]` if not specified.\n\n### Controller Configuration\n\nThe controller dialer is disabled by default and must be explicitly enabled. When enabled, the\ncontroller will dial routers that have control channel listeners configured and are not already\nconnected.\n\n```yaml\nctrl:\n  dialer:\n    enabled: true\n    groups:\n      - default\n    dialDelay: 30s\n    minRetryInterval: 1s\n    maxRetryInterval: 5m\n    retryBackoffFactor: 1.5\n    fastFailureWindow: 5s\n    queueSize: 32\n    maxWorkers: 10\n```\n\n- `enabled` - Enables the controller dialer (default: `false`)\n- `groups` - List of groups to match against router listener groups (default: `[\"default\"]`)\n- `dialDelay` - Delay before the controller starts dialing after boot (default: `30s`)\n- `minRetryInterval` - Minimum backoff delay between dial retries (default: `1s`)\n- `maxRetryInterval` - Maximum backoff delay between dial retries (default: `5m`)\n- `retryBackoffFactor` - Multiplier applied to the retry delay on each failure, jittered +/- 0.5 (default: `1.5`)\n- `fastFailureWindow` - If a connection drops within this window after connecting, backoff continues rather than resetting (default: `5s`)\n- `queueSize` - Maximum number of pending dial jobs in the worker pool queue (default: `32`)\n- `maxWorkers` - Maximum number of concurrent dial workers (default: `10`)\n\nThe controller will only dial routers whose listener groups overlap with the controller's configured\ngroups. When a router advertises multiple ctrl channel listener addresses, the dialer rotates through\nthem on each failure so that an unreachable address does not block attempts to the others.\n\n### Metrics\n\nThe controller dialer worker pool exposes the following metrics under the `ctrl_channel.dialer` prefix:\n\n- `ctrl_channel.dialer.queue_size` - Current number of pending dial jobs in the queue\n- `ctrl_channel.dialer.worker_count` - Current number of dial worker goroutines\n- `ctrl_channel.dialer.busy_workers` - Number of workers currently executing a dial\n- `ctrl_channel.dialer.work_timer` - Timer tracking the duration of each dial attempt\n\n## SDK Inspection Support\n\nNew CLI commands have been added for inspecting SDK state, useful for diagnosing terminator and\nconnectivity issues.\n\n**Note:** SDK inspection requires SDK support. Currently only the Go SDK supports inspection,\nas of version 1.5.0. Other SDKs will need to add support before these commands can be used\nwith them.\n\n### `ziti fabric inspect sdk`\n\nRetrieves SDK context inspection data from identities connected to routers.\n\n```\nziti fabric inspect sdk <target-selector> <identity-id>\n```\n\nThe `<target-selector>` is a regex matching router IDs (use `.*` for all routers). The\n`<identity-id>` is the identity whose SDK context you want to inspect. The command returns\ndetailed state from the connected SDK instance, including active services, terminators, and\nconnection status.\n\n### `ziti agent tunnel dump-sdk`\n\nDumps SDK context information from a running `ziti tunnel` process via the IPC agent.\n\n```\nziti agent tunnel dump-sdk\n```\n\nReturns JSON-formatted inspection data for all SDK contexts registered in the tunnel process,\nincluding service listeners, connections, and terminator state.\n\n## Current Beta Features\n\n* Basic Permission System\n* Alert Events\n* Controller-Initiated Control Channel Dials\n\n## Revocation System Improvements\n\nWhen a session is refreshed, the old refresh token's revocation is no longer created\nsynchronously through raft. Instead, revocations are queued in memory and flushed in\nbatches on a configurable interval. This removes the database and raft as a bottleneck\non token refreshes. If the old token is close to expiring, the revocation is skipped\nentirely.\n\nNew configuration tunables under `edge.oidc`:\n\n| Key | Default | Description |\n|-----|---------|-------------|\n| `revocationMinTokenLifetime` | unset | Skip revocation if the old token expires within this duration (must be < 50% of `refreshTokenDuration`) |\n| `revocationBucketInterval` | `1m` | Bucket window for batching revocations before flushing through raft |\n| `revocationBucketMaxSize` | `200` | Max revocations per raft entry |\n| `revocationMaxQueued` | `25000` | Max revocations queued in memory before dropping |\n| `revocationEnforcerFrequency` | `1m` | How often expired revocations are purged (leader only) |\n\n## Component Updates and Bug Fixes\n\n* github.com/openziti/agent: [v1.0.31 -> v1.0.33](https://github.com/openziti/agent/compare/v1.0.31...v1.0.33)\n* github.com/openziti/channel/v4: [v4.2.28 -> v4.3.9](https://github.com/openziti/channel/compare/v4.2.28...v4.3.9)\n    * [Issue #235](https://github.com/openziti/channel/issues/235) - Bump allowed hello message headers size to 16k from 4k\n    * [Issue #228](https://github.com/openziti/channel/issues/228) - Ensure that Underlay never return nil on MultiChannel\n    * [Issue #226](https://github.com/openziti/channel/issues/226) - Allow specifying a minimum number of underlays for a channel, regardless of underlay type\n    * [Issue #225](https://github.com/openziti/channel/issues/225) - Add ChannelCreated to the UnderlayHandler API to allow handlers to be initialized with the channel before binding\n    * [Issue #224](https://github.com/openziti/channel/issues/224) - Update the underlay dispatcher to allow unknown underlay types to fall through to the default\n    * [Issue #222](https://github.com/openziti/channel/issues/222) - Allow injecting the underlay type into messages\n\n* github.com/openziti/edge-api: [v0.26.47 -> v0.27.5](https://github.com/openziti/edge-api/compare/v0.26.47...v0.27.5)\n    * [Issue #175](https://github.com/openziti/edge-api/issues/175) - ctrlChanListeners should have x-omit-empty: false attribute\n    * [Issue #170](https://github.com/openziti/edge-api/issues/170) - Add preferredLeader flag to controllers\n    * [Issue #167](https://github.com/openziti/edge-api/issues/167) - Add ctrlChanListeners to router types\n    * [Issue #164](https://github.com/openziti/edge-api/issues/164) - Add permissions list to identity\n\n* github.com/openziti/foundation/v2: [v2.0.72 -> v2.0.90](https://github.com/openziti/foundation/compare/v2.0.72...v2.0.90)\n    * [Issue #472](https://github.com/openziti/foundation/issues/472) - Add support for multi-bit set/get to AtomicBitSet\n    * [Issue #464](https://github.com/openziti/foundation/issues/464) - Add support for -pre in versions\n    * [Issue #455](https://github.com/openziti/foundation/issues/455) - Correctly close goroutine pool when external close is signaled\n    * [Issue #452](https://github.com/openziti/foundation/issues/452) - Goroutine pool with a min worker count of 1 can drop to 0 workers due to race condition\n\n* github.com/openziti/identity: [v1.0.111 -> v1.0.128](https://github.com/openziti/identity/compare/v1.0.111...v1.0.128)\n    * [Issue #68](https://github.com/openziti/identity/issues/68) - Shutdown file watcher when stopping identity watcher\n\n* github.com/openziti/metrics: [v1.4.2 -> v1.4.5](https://github.com/openziti/metrics/compare/v1.4.2...v1.4.5)\n    * [Issue #58](https://github.com/openziti/metrics/issues/58) - Add GaugeFloat64 support\n    * [Issue #56](https://github.com/openziti/metrics/issues/56) - underlying resources of reference counted meters are not cleaned up when reference count hits zero\n\n* github.com/openziti/runzmd: [v1.0.80 -> v1.0.90](https://github.com/openziti/runzmd/compare/v1.0.80...v1.0.90)\n* github.com/openziti/sdk-golang: [v1.2.3 -> v1.5.4](https://github.com/openziti/sdk-golang/compare/v1.2.3...v1.5.4)\n    * [Issue #902](https://github.com/openziti/sdk-golang/issues/902) - Inspect response message content types are mixed up\n    * [Issue #887](https://github.com/openziti/sdk-golang/issues/887) - Fix listener manager cleanup\n    * [Issue #886](https://github.com/openziti/sdk-golang/issues/886) - When controller is busy during service refresh, backoff and retry instead of falling back to full refresh\n    * [Issue #885](https://github.com/openziti/sdk-golang/issues/885) - Only compare relevant service fields when looking for changes\n    * [Issue #884](https://github.com/openziti/sdk-golang/issues/884) - Add deadline for bind establishment\n    * [Issue #883](https://github.com/openziti/sdk-golang/issues/883) - Router level listener can be left open if multi-listener closes during listener establishment\n    * [Issue #877](https://github.com/openziti/sdk-golang/issues/877) - Handle differences in xgress eof/end-of-circuit handling by adding a capabilities exchange\n    * [Issue #832](https://github.com/openziti/sdk-golang/issues/832) - Fuzz session refresh timers\n    * [Issue #879](https://github.com/openziti/sdk-golang/issues/879) - Return the connId in inspect response\n    * [Issue #878](https://github.com/openziti/sdk-golang/issues/878) - Fix responses from rx goroutines\n    * [Issue #874](https://github.com/openziti/sdk-golang/issues/874) - Add inspect support at the context level\n    * [Issue #871](https://github.com/openziti/sdk-golang/issues/871) - Make SDK better at sticking to MaxTerminator terminators\n    * [Issue #708](https://github.com/openziti/sdk-golang/issues/708) - Support for Go's built-in context in Dial methods\n    * [Issue #860](https://github.com/openziti/sdk-golang/issues/860) - Make the dialing identity's id and name available on dialed connections\n    * [Issue #857](https://github.com/openziti/sdk-golang/issues/857) - Use new error code and retry hints to correctly react to terminator errors\n    * [Issue #847](https://github.com/openziti/sdk-golang/issues/847) - Ensure the initial version check succeeds, to ensure we don't legacy sessions on ha or oidc-enabled controllers\n    * [Issue #824](https://github.com/openziti/sdk-golang/pull/824) - release notes and hard errors on no TOTP handler breaks partial auth events\n    * [Issue #818](https://github.com/openziti/sdk-golang/issues/818) - Full re-auth should not clear services list, as that breaks the on-change logic\n    * [Issue #817](https://github.com/openziti/sdk-golang/issues/817) - goroutines can get stuck when iterating over randomized HA controller list\n    * [Issue #736](https://github.com/openziti/sdk-golang/issues/736) - Migrate from github.com/mailru/easyjson\n    * [Issue #813](https://github.com/openziti/sdk-golang/issues/813) - SDK doesn't stop close listener when it detects that a service being hosted gets deleted\n    * [Issue #811](https://github.com/openziti/sdk-golang/issues/811) - Credentials are lost when explicitly set\n    * [Issue #807](https://github.com/openziti/sdk-golang/issues/807) - Don't send close from rxer to avoid blocking\n    * [Issue #800](https://github.com/openziti/sdk-golang/issues/800) - Tidy create service session logging\n\n* github.com/openziti/secretstream: [v0.1.39 -> v0.1.49](https://github.com/openziti/secretstream/compare/v0.1.39...v0.1.49)\n* github.com/openziti/storage: [v0.4.26 -> v0.4.39](https://github.com/openziti/storage/compare/v0.4.26...v0.4.39)\n    * [Issue #122](https://github.com/openziti/storage/issues/122) - StringFuncNode has incorrect nil check, allowing panic\n    * [Issue #120](https://github.com/openziti/storage/issues/120) - Change post tx commit constraint handling order\n    * [Issue #119](https://github.com/openziti/storage/issues/119) - Add ContextDecorator API\n\n* github.com/openziti/transport/v2: [v2.0.188 -> v2.0.215](https://github.com/openziti/transport/compare/v2.0.188...v2.0.215)\n    * [Issue #31](https://github.com/openziti/transport/issues/31) - ipv6 Transport Address Parsing\n    * [Issue #149](https://github.com/openziti/transport/issues/149) - Archive transwarp code\n\n* github.com/openziti/xweb/v3: [v2.3.4 -> v3.0.4](https://github.com/openziti/xweb/compare/v2.3.4...v3.0.4)\n    * [Issue #32](https://github.com/openziti/xweb/issues/32) - watched identities sometimes don't reload when changed\n\n* github.com/openziti/go-term-markdown: v1.0.1 (new)\n* github.com/openziti/ziti/v2: [v1.6.8 -> v2.0.0](https://github.com/openziti/ziti/compare/v1.6.8...v2.0.0)\n    * [Issue #3681](https://github.com/openziti/ziti/issues/3681) - coalesce OIDC JWT revocations to reduce controller write pressure\n    * [Issue #3683](https://github.com/openziti/ziti/issues/3683) - add fablab test for testing flow control changes over a longer term\n    * [Issue #3673](https://github.com/openziti/ziti/issues/3673) - revocation build-up in db and rdm\n    * [Issue #3674](https://github.com/openziti/ziti/issues/3674) - Update to Go 1.26\n    * [Issue #3496](https://github.com/openziti/ziti/issues/3496) - MFA TOTP Enrollment During OIDC Authentication Does Not Work\n    * [Issue #3609](https://github.com/openziti/ziti/issues/3609) - Stabilize terminator creation test for 2.0\n    * [Issue #3648](https://github.com/openziti/ziti/issues/3648) - tunnel: myCopy logs router ID as circuitId, causing misleading debug output\n    * [Issue #3658](https://github.com/openziti/ziti/issues/3658) - Raft cluster join fails with \"hello message too big\" when using long hostnames\n    * [Issue #3626](https://github.com/openziti/ziti/issues/3626) - controller: overlay bind point produces malformed URL in /versions apiBaseUrls\n    * [Issue #3635](https://github.com/openziti/ziti/issues/3635) - Allow controllers to dial routers to support more topologies\n    * [Issue #3607](https://github.com/openziti/ziti/issues/3607) - linux installer not upgradable from v1\n    * [Issue #3650](https://github.com/openziti/ziti/issues/3650) - Reroute doesn't proactively clean up orphaned route entries\n    * [Issue #3571](https://github.com/openziti/ziti/issues/3571) - Ensure 2.0 backwards compatibility with 1.6 and 1.5 using the smoketest\n    * [Issue #3636](https://github.com/openziti/ziti/issues/3636) - Adaptive rate limiter should use success rate rather than queue position\n    * [Issue #3600](https://github.com/openziti/ziti/issues/3600) - Add a preferredLeader flag, allow selected nodes to be preferred for raft leader, if they're available\n    * [Issue #3642](https://github.com/openziti/ziti/issues/3642) - Use xgress_common.Connection type for xgress_transport and xgress_proxy\n    * [Issue #3597](https://github.com/openziti/ziti/issues/3597) - Enable OIDC API by default\n    * [Issue #3624](https://github.com/openziti/ziti/issues/3624) - Multi-underlay control channel doesn't correctly handle lack of group secret on non-grouped underlays\n    * [Issue #3613](https://github.com/openziti/ziti/issues/3613) - Initializing cluster from existing db using `db:` config settings results in panic\n    * [Issue #3620](https://github.com/openziti/ziti/issues/3620) - Controller control channel heartbeats config parsing was erroneously nested under options parsing\n    * [Issue #3619](https://github.com/openziti/ziti/issues/3619) - Router connect events full sync interval was using min/max values meant for the batch interval\n    * [Issue #3618](https://github.com/openziti/ziti/issues/3618) - Router interfaceDiscovery.minReportInterval value was being set to checkInterval\n    * [Issue #3617](https://github.com/openziti/ziti/issues/3617) - Transit router disabled flag not passed through raft command structure\n    * [Issue #3333](https://github.com/openziti/ziti/issues/3333) - Updb user-lockout triggered by successful login attempts\n    * [Issue #3599](https://github.com/openziti/ziti/issues/3599) - Add gap detection and handling to router data model\n    * [Issue #3356](https://github.com/openziti/ziti/issues/3356) - Add WWW-Authenticate Headers\n    * [Issue #3074](https://github.com/openziti/ziti/issues/3074) - Dynamic cost range is too limited\n    * [Issue #3558](https://github.com/openziti/ziti/issues/3558) - terminator cost increased on egress dial success, not on circuit completion\n    * [Issue #3556](https://github.com/openziti/ziti/issues/3556) - global circuit costs not cleared when terminator is deleted\n    * [Issue #3557](https://github.com/openziti/ziti/issues/3557) - costing calculation for the weighted terminator selection strategy  is incorrect\n    * [Issue #2512](https://github.com/openziti/ziti/issues/2512) - Return circuit ID and error in dial failures\n    * [Issue #3569](https://github.com/openziti/ziti/issues/3569) - Version 2.0+ routers should not connect to controllers which do not support JWT formatted legacy sessions\n    * [Issue #3565](https://github.com/openziti/ziti/issues/3565) - Link dialer save 'is first conn' true, so all dials claim to be first, causing potential race condition\n    * [Issue #3550](https://github.com/openziti/ziti/issues/3550) - Support multi-underlay control channels\n    * [Issue #3535](https://github.com/openziti/ziti/issues/3535) - Remove the legacy xgress_edge_tunnel implementation\n    * [Issue #3547](https://github.com/openziti/ziti/issues/3547) - Add support for sending the dialing identity id and name to the hosting sdk\n    * [Issue #3541](https://github.com/openziti/ziti/issues/3541) - Remove option to disable the router data model in the controller\n    * [Issue #3540](https://github.com/openziti/ziti/issues/3540) - Handle UDP difference between proxy and tproxy implementations\n    * [Issue #3527](https://github.com/openziti/ziti/issues/3527) - ER/T UDP tunnels keep closed connections for 30s, preventing potential new good connections in that time\n    * [Issue #3526](https://github.com/openziti/ziti/issues/3526) - ER/T half-close logic is incorrect\n    * [Issue #3524](https://github.com/openziti/ziti/issues/3524) - Provide more error context to SDKs for terminator errors\n    * [Issue #3509](https://github.com/openziti/ziti/issues/3509) - Enforce policy on the router for oidc sessions, by closing open circuits and terminators when service access is lost\n    * [Issue #3531](https://github.com/openziti/ziti/issues/3531) - Remove created/updated/deleted terminator events. Obsoleted by entity change events.\n    * [Issue #3532](https://github.com/openziti/ziti/issues/3532) - Removed deprecated create identity <type> subcommands\n    * [Issue #3521](https://github.com/openziti/ziti/issues/3521) - Cleanup CLI to remove calls to os.Exit to be embed friendlier\n    * [Issue #3516](https://github.com/openziti/ziti/issues/3516) - Remove support for create terminator v1\n    * [Issue #3512](https://github.com/openziti/ziti/issues/3512) - Remove legacy link management code from the controller\n    * [Issue #3511](https://github.com/openziti/ziti/issues/3511) - router proxy mode fails to resolve interface if binding is 0.0.0.0\n    * [Issue #3503](https://github.com/openziti/ziti/issues/3503) - Allow routers to request current cluster membership information\n    * [Issue #3501](https://github.com/openziti/ziti/issues/3501) - Get cluster membership information from raft directly, rather than trying to cache it in the DB\n    * [Issue #3500](https://github.com/openziti/ziti/issues/3500) - Set a router data model timeline when initializing a new HA setup, rather than letting it stay blank\n    * [Issue #3504](https://github.com/openziti/ziti/issues/3504) - Reduce router data model full state updates\n    * [Issue #3492](https://github.com/openziti/ziti/pull/3492) - Bump openziti/ziti-console-assets from 3.12.9 to 4.0.0 in /dist/docker-images/ziti-controller in the all group\n    * [Issue #3484](https://github.com/openziti/ziti/issues/3484) - router ctrl channel handler for handling cluster changes has an initialization race condition\n    * [Issue #3477](https://github.com/openziti/ziti/issues/3477) - Optionally enable model changes triggered by login to be non-blocking and to be droppable if the system is under load\n    * [Issue #3473](https://github.com/openziti/ziti/issues/3473) - Enable tls handshake rate limiter by default and tweak default values.\n    * [Issue #3471](https://github.com/openziti/ziti/issues/3471) - Go tunneler is ignoring host config MaxConnections\n    * [Issue #3469](https://github.com/openziti/ziti/issues/3469) - Only send model updates on resubscribe if the RDM index has advanced\n    * [Issue #2573](https://github.com/openziti/ziti/issues/2573) - An edge router in a tight restart loop causes a resource leak on routers to which it connects.\n    * [Issue #3430](https://github.com/openziti/ziti/issues/3430) - Add permissions list to identity\n    * [Issue #2109](https://github.com/openziti/ziti/issues/2109) - Add Edge Management Read Only Capability\n    * [Issue #3435](https://github.com/openziti/ziti/issues/3435) - Add edge management API permissions by entity type and action\n    * [Issue #3441](https://github.com/openziti/ziti/issues/3441) - Update router connection tracker to interrogate active connections\n    * [Issue #3451](https://github.com/openziti/ziti/issues/3451) - ci - compare only stable releases when promoting\n    * [Issue #3437](https://github.com/openziti/ziti/issues/3437) - SDK OIDC token updates to routers should return an error if invalid\n    * [Issue #3348](https://github.com/openziti/ziti/issues/3348) - Unable to clear/reset the \"tags\" property on an entity to an empty object\n    * [Issue #3452](https://github.com/openziti/ziti/issues/3452) - `ziti agent cluster add` has bad behavior if the add address doesn't match the advertise address\n    * [Issue #3410](https://github.com/openziti/ziti/issues/3410) - Consolidate fabric REST API code with edge management and edge client code\n    * [Issue #3425](https://github.com/openziti/ziti/issues/3425) - RDM not properly responding to tunneler enabled flag changes\n    * [Issue #3420](https://github.com/openziti/ziti/issues/3420) - The terminator id cache uses the same id for all terminators in a host.v2 config, resulting in a single terminator\n    * [Issue #3419](https://github.com/openziti/ziti/issues/3419) - When using the router data model, precedence specified on the per-service identity mapping are incorrectly interpreted\n    * [Issue #3318](https://github.com/openziti/ziti/issues/3318) - Terminator creation seems to slow exponentially as the number of terminators rises from 10k to 20k to 40k\n    * [Issue #3407](https://github.com/openziti/ziti/issues/3407) - The CLI doesn't properly pass JWT authentication information to websocket endpoints\n    * [Issue #3359](https://github.com/openziti/ziti/issues/3359) - Ensure router data model subscriptions have reasonable performance and will scale\n    * [Issue #3354](https://github.com/openziti/ziti/issues/3354) - SDK/ENV Info from SDKs is not distributed in HA\n    * [Issue #3381](https://github.com/openziti/ziti/issues/3381) - the fabric service REST apis are missing the maxIdleTime property\n    * [Issue #3382](https://github.com/openziti/ziti/issues/3382) - Legacy service sessions generated pre-1.7.x are incompatible with v1.7.+ and need to be cleared\n    * [Issue #3339](https://github.com/openziti/ziti/issues/3339) - get router ctrl.endpoint from ctrls claim in JWT\n    * [Issue #3378](https://github.com/openziti/ziti/issues/3378) - login with file stopped working\n    * [Issue #3349](https://github.com/openziti/ziti/issues/3349) - UPDB OIDC login returns wrong content type\n    * [Issue #2324](https://github.com/openziti/ziti/issues/2324) - Add Ext-JWT/OIDC enrollment\n    * [Issue #3346](https://github.com/openziti/ziti/issues/3346) - Fix confusing attempt logging\n    * [Issue #3337](https://github.com/openziti/ziti/issues/3337) - Router reports \"no xgress edge forwarder for circuit\"\n    * [Issue #3345](https://github.com/openziti/ziti/issues/3345) - Clean up connect events tests and remove global XG registry\n    * [Issue #3264](https://github.com/openziti/ziti/issues/3264) - Allow routers to generate alert events in cases of service misconfiguration\n    * [Issue #3321](https://github.com/openziti/ziti/issues/3321) - Health Check API missing base path on discovery endpoint\n    * [Issue #3323](https://github.com/openziti/ziti/issues/3323) - router/tunnel static services fail to bind unless new param protocol is defined\n    * [Issue #3309](https://github.com/openziti/ziti/issues/3309) - Detect link connections meant for another router\n    * [Issue #3286](https://github.com/openziti/ziti/issues/3286) - edge-api binding doesn't have the correct path on discovery endpoints\n    * [Issue #3297](https://github.com/openziti/ziti/issues/3297) - stop promoting hotfixes downstream\n    * [Issue #3295](https://github.com/openziti/ziti/issues/3295) - make ziti tunnel service:port pairs optional\n    * [Issue #3291](https://github.com/openziti/ziti/issues/3291) - replace decommissioned bitnami/kubectl\n    * [Issue #3277](https://github.com/openziti/ziti/issues/3277) - Router can deadlock on closing a connection if the incoming data channel is full\n    * [Issue #3269](https://github.com/openziti/ziti/issues/3269) - Add host-interfaces config type\n    * [Issue #3258](https://github.com/openziti/ziti/issues/3258) - Add config type proxy.v1 so proxies can be defined dynamically for the ER/T\n    * [Issue #3259](https://github.com/openziti/ziti/issues/3259) - Interfaces config type not added due to wrong name\n    * [Issue #3265](https://github.com/openziti/ziti/issues/3265) - Forwarding errors should log at debug, since they are usual part of circuit teardown\n    * [Issue #3261](https://github.com/openziti/ziti/issues/3261) - ER/T dialed xgress connections may only half-close when peer is fully closed\n    * [Issue #3207](https://github.com/openziti/ziti/issues/3207) - Allow router embedders to customize config before start\n\n\n","mentions_count":1},{"url":"https://api.github.com/repos/openziti/ziti/releases/299045650","assets_url":"https://api.github.com/repos/openziti/ziti/releases/299045650/assets","upload_url":"https://uploads.github.com/repos/openziti/ziti/releases/299045650/assets{?name,label}","html_url":"https://github.com/openziti/ziti/releases/tag/v2.0.0-pre6","id":299045650,"author":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"node_id":"RE_kwDODVFMN84R0xMS","tag_name":"v2.0.0-pre6","target_commitish":"main","name":"v2.0.0-pre6","draft":false,"immutable":false,"prerelease":true,"created_at":"2026-03-19T16:10:30Z","updated_at":"2026-03-19T16:25:46Z","published_at":"2026-03-19T16:25:46Z","assets":[{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/377305739","id":377305739,"node_id":"RA_kwDODVFMN84WfTqL","name":"checksums.sha256.txt","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"text/plain; charset=utf-8","state":"uploaded","size":790,"digest":"sha256:5ab7f5650fd3874e7ceb045cde8bac159b78f53241c193e74c60d68adaf3dd0e","download_count":2,"created_at":"2026-03-19T16:25:44Z","updated_at":"2026-03-19T16:25:44Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre6/checksums.sha256.txt"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/377305741","id":377305741,"node_id":"RA_kwDODVFMN84WfTqN","name":"sbom-v2.0.0-pre6.spdx.json","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/json","state":"uploaded","size":1003509,"digest":"sha256:7bd766a1de3102c4077601b80e52ed79b49a77f2542e0a9da479768dec125786","download_count":0,"created_at":"2026-03-19T16:25:44Z","updated_at":"2026-03-19T16:25:44Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre6/sbom-v2.0.0-pre6.spdx.json"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/377305740","id":377305740,"node_id":"RA_kwDODVFMN84WfTqM","name":"source-v2.0.0-pre6.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":3794800,"digest":"sha256:c5d1f796950e3e1fa4af894a6d243f721d7630b84b1bfca191bdc71101cfbcc7","download_count":1,"created_at":"2026-03-19T16:25:44Z","updated_at":"2026-03-19T16:25:44Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre6/source-v2.0.0-pre6.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/377305718","id":377305718,"node_id":"RA_kwDODVFMN84WfTp2","name":"ziti-darwin-amd64-2.0.0-pre6.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":53942898,"digest":"sha256:e14051a8d35b5cfb00d65414e59c44372eb0ce84d5387317e8e196bbe9b6c9ba","download_count":3,"created_at":"2026-03-19T16:25:41Z","updated_at":"2026-03-19T16:25:43Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre6/ziti-darwin-amd64-2.0.0-pre6.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/377305717","id":377305717,"node_id":"RA_kwDODVFMN84WfTp1","name":"ziti-darwin-arm64-2.0.0-pre6.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":50302602,"digest":"sha256:b940307e2ee218c81e527ce5b36686e2ff8c2b8d7e55f6e1ccfe5b8983135ecd","download_count":1,"created_at":"2026-03-19T16:25:41Z","updated_at":"2026-03-19T16:25:43Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre6/ziti-darwin-arm64-2.0.0-pre6.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/377305715","id":377305715,"node_id":"RA_kwDODVFMN84WfTpz","name":"ziti-linux-amd64-2.0.0-pre6.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":55252360,"digest":"sha256:133557a8cb363925f67576f339b01f229e635c71b12b7074c13a1dbdc251f265","download_count":21,"created_at":"2026-03-19T16:25:41Z","updated_at":"2026-03-19T16:25:43Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre6/ziti-linux-amd64-2.0.0-pre6.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/377305714","id":377305714,"node_id":"RA_kwDODVFMN84WfTpy","name":"ziti-linux-arm-2.0.0-pre6.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":51747544,"digest":"sha256:658066854c3e17ea843ff0c8bfea2b349922725642c3070637826414579e84b7","download_count":3,"created_at":"2026-03-19T16:25:41Z","updated_at":"2026-03-19T16:25:43Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre6/ziti-linux-arm-2.0.0-pre6.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/377305735","id":377305735,"node_id":"RA_kwDODVFMN84WfTqH","name":"ziti-linux-arm64-2.0.0-pre6.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":51761755,"digest":"sha256:3093e7080d9f59b38ee901921ad77e5c3d3384d0b2bb2dca0a60d5ba71db4a07","download_count":2,"created_at":"2026-03-19T16:25:43Z","updated_at":"2026-03-19T16:25:45Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre6/ziti-linux-arm64-2.0.0-pre6.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/377305716","id":377305716,"node_id":"RA_kwDODVFMN84WfTp0","name":"ziti-windows-amd64-2.0.0-pre6.zip","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/zip","state":"uploaded","size":44640695,"digest":"sha256:22b8f65a36f0e1566bcda026810c8d4674f38dea8a15ee20ac0fb5c8ba3dde66","download_count":3,"created_at":"2026-03-19T16:25:41Z","updated_at":"2026-03-19T16:25:43Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre6/ziti-windows-amd64-2.0.0-pre6.zip"}],"tarball_url":"https://api.github.com/repos/openziti/ziti/tarball/v2.0.0-pre6","zipball_url":"https://api.github.com/repos/openziti/ziti/zipball/v2.0.0-pre6","body":"# Release 2.0.0\n\n## What's New\n\nThis is the next major version release of OpenZiti, following the 1.0 release in April 2024.\nOf particular note is that HA controllers are now considered ready for general use.\nThis release also introduces a new permissions model, OIDC/JWT token-based enrollment, \nclustering performance improvements, and a number of other features and fixes. Because \nsome of these changes are not backwards compatible with older routers, we're marking this \nas a major version bump.\n\n### HA Controllers are now considered ready for general use\n\nThis is a pretty big milestone and marks the completion of work that's been ongoing for a couple of years.\nThe HA work has brought with it some notable changes. Authentication now uses JWTs by default. Using JWTs\nmeans the controllers don't need to store session and propagate them to the routers. This removes a bottleneck\nfrom the network and allows the load to be more easily distributed among controllers and routers.\n\nTo support distributed authentication, the routers now get a bespoke version of the data model. In addition\nto enabling distributed authentication this will allow us to remove the need for service polling and further \nreduce the load on controllers in the future.\n\n### Router Compatibility\n\nRelated to the JWT work, routers with version 2.+ will only work with controllers that are version 2.+. This means\nto upgrade your network, controllers should be upgraded first. Routers can then be upgraded individually.\n\n2.x routers should still work fine with older router versions.\n\nWe try very hard to avoid breaking changes like this, but sometimes the engineering trade-offs lead there. This change\nwas first made in the 1.7 release. That release has not been marked stable, and we have no plans to do so, because\nof the backwards incompatibility. \n\nIf you need to support in the 1.6.12+ range, see the section \"OIDC enabled by default\".\n\n### New Permissions Model (BETA)\n\nAs one feature goes out of beta, another arrives into beta. This release introduces a new permissions system\nfor more fine grained control to the management API. It's not expected to change, but may do so based on feedback\nfrom users.\n\n### Updated Release Process\n\nWe have moved to creating `-preN` releases for major and minor versions. This way we can put out release candidates,\nor feature previews and put them through internal testing and let interested folks from the community try them out.\nThen, when we're ready, we can run the full validation suite against the last pre-release and retag it. \n\nPatch releases won't have `-preN` and should contain only high priority bug fixes.\n\n### Deprecation Cleanup\n\nSince we already have a breaking change, we're removing some other backwards compatibility code.\n\n* Controller managed links \n    * Router managed links were introduced in v0.30.0. \n    * If you're upgrading from an older versions, you'll want to upgrade to the latest 1.x release before jumping to 2.x\n    * Github tracking issue: https://github.com/openziti/ziti/issues/3512\n* `ziti edge create identity <type>`\n    * Identity types other than router were removed in v0.30.2\n    * The `type` can be dropped from the CLI command\n    * Github tracking issue: https://github.com/openziti/ziti/issues/3532\n* Terminator create/update/delete events\n    * These have been superseded by entity change events, which also have create/update/delete events for terminators\n    * Entity change events were introduced in v0.28.0\n    * Github tracking issue: https://github.com/openziti/ziti/issues/3531\n* `xgress_edge_tunnel` v1\n    * This is the first implementation of the tunneler in edge-router code (ER/T) which used legacy api sessions and services\n    * The v2 version uses the router data model and was introduced in v0.30.x\n    * Github tracking issue: https://github.com/openziti/ziti/issues/3516\n\n### Legacy Session Deprecation\n\nOIDC sessions are now preferred. They are the default, or will become the default for SDKs and tunnelers. They are also required\nwhen running HA. Legacy API and service session are now deprecated and will be removed in the OpenZiti v3.0.0 release. \n\n### Additional Features\n\n* Controllers can now optionally bind APIs using an OpenZiti identity\n* `ziti edge login` now supports the `--network-identity` flag to authenticate and establish connections through the Ziti overlay network\n* `ziti edge login` now supports using a bearer token with `--token` for authentication. The token is expected to be\n  provided as just the JWT, not with the \"Bearer \" prefix\n* Identity configuration can now be loaded from files or environment variables for flexible deployment scenarios\n* Identities can now be provisioned just-in-time through OIDC/JWT token-based enrollment\n* Multiple model updates can now be in-flight at the same time, improving clustering performance\n* Authentication-related model updates can now be non-blocking and even dropped if the system is too busy\n* Routers now provide more error context to SDKs for terminator errors, enabling better retry behavior\n* New `proxy.v1` config type for dynamic service proxies (originally released in 1.7.0)\n* New alert event type for surfacing operational issues to network operators - Beta (originally released in 1.7.0)\n* New Azure Service Bus event sink for streaming controller events, contributed by @ffaraone (originally released in 1.7.0)\n* Bundled ZAC upgraded to 4.0\n* Build updated to Go 1.25\n* CLI cleaned up to remove calls to `os.Exit`, making it more friendly for embedding\n* OIDC is now enabled by default\n* Controller Edge APIs now return `WWW-Authenticate` response headers on `401 Unauthorized` responses, giving clients actionable information about which auth methods are accepted and what went wrong\n* HA Controllers can be marked as 'preferredLeader' via config\n* Dynamic cost range for smart routing expanded beyond the previous 64K limit\n* Dial failures now return the circuit ID and error information for easier debugging\n* Router-to-controller control channels now support multiple underlays with priority-based message routing\n* The dialing identity's ID and name are now forwarded to the hosting SDK\n* Controllers can now dial routers to establish control channels, enabling connectivity when routers are behind firewalls (Beta)\n* Refresh-token revocations are now batched and best-effort, removing the database/raft bottleneck on token refreshes\n\n## Basic Permission System (BETA)\n\nAdded a basic permission system that allows more control over identity access to controller management API operations. \nThis replaces the previous binary admin/non-admin model with a more flexible permission system.\n\n**NOTE:** This feature is in BETA, primarily so we can get feedback on which permissions make sense. The implementation is unlikely to change\nbut the set of exposed permissions may grow, shrink or change based on user feedback. \n\n### Permission Model\n\nThe permission system supports three levels of authorization:\n\n  1. **Global Permissions**: System-wide access levels\n     - `admin` - Full access to all operations. This is still controlled by the `isAdmin` flag on identity\n     - `admin_readonly` - Read-only access to all resources except debugging facilities inspect and validate\n\n  2. **Entity-Level Permissions**: Full CRUD access to specific entity types\n     - Granting an entity-level permission (e.g., `service`) provides complete create, read, update, and delete access for that entity type\n\n  3. **Action-Level Permissions**: Specific operation access on entity types\n     - Fine-grained control using the pattern `<entity>.<action>` (e.g., `service.read`, `identity.update`)\n     - Supports `create`, `read`, `update`, and `delete` actions per entity type\n\n### Supported Entity Permissions\n\nThe following entity-level permissions are available:\n\n- `auth-policy` - Authentication policy management\n- `ca` - Certificate Authority management\n- `config` - Configuration management\n- `config-type` - Configuration type management\n- `edge-router-policy` - Edge router policy management\n- `enrollment` - Enrollment management\n- `external-jwt-signer` - External JWT signer management\n- `identity` - Identity management\n- `posture-check` - Posture check management\n- `router` - Edge and transit router management\n- `service` - Service management\n- `service-policy` - Service policy management\n- `service-edge-router-policy` - Service edge router policy management\n- `terminator` - Terminator management\n- `ops` - Operational resources (API sessions, sessions, circuits, links, inspect and validate)\n\n### Permission Assignment\n\nPermissions are assigned to identities via the `permissions` field in the identity resource. Multiple permissions can be granted to a single identity, and permissions are additive.\n\n### Cross-Entity Operations\n\nListing related entities through an entity's endpoints requires appropriate permissions for the related entity type. For example:\n- Listing services for a service-policy requires `service.read` permission\n- Listing identities for an edge-router-policy requires `identity.read` permission\n- Listing configs for a service requires `config.read` permission\n\n**NOTE:** \nMore permissions than expected may be required when performing actions through the CLI or ZAC. Take for example, when an identity \nhas `config.create` and is attempting to create a new config. The CLI may fail if the identity doesn't have `config-type.read`\nas well because it will need to look up the config type id that corresponds to the given config type name.\n\nSimilar cross entity read permissions may be required when creating services.\n\n### Admin Protection\n\nNon-admin identities cannot:\n- Create identities with the `isAdmin` flag\n- Create identities with any permissions granted\n- Modify admin-related fields on existing identities\n- Update or delete admin identities\n- Grant permissions to identities\n\nThese protections ensure that privilege escalation is prevented and admin access remains controlled.\n\n\n## Binding Controller APIs With Identity\n\nController APIs can now be bound to an OpenZiti overlay network identity, allowing secure communication through\nthe Ziti network. This is useful for scenarios where you want to expose controller APIs only through the overlay\nnetwork rather than on a standard network interface.\n\n### Configuration Structure\n\nA standard `bindPoint` configuration looks like this:\n```text\n    bindPoints:\n      - interface: 127.0.0.1:18441\n        address: 127.0.0.1:18441\n```\n\nTo bind controller APIs to an OpenZiti identity, add an additional `identity` block to your `bindPoints`. The\nidentity configuration specifies where to load the Ziti identity file and which service to bind it to:\n\n```text\n    bindPoints:\n      - interface: 127.0.0.1:18441\n        address: 127.0.0.1:18441\n      - identity:\n          file: \"c:/temp/ctrl.testing/ctrl.identity.json\"\n          service: \"mgmt\"\n```\n\n### Supported Configuration Options\n\n- `file`: Path to a Ziti identity JSON file containing the controller's identity and enrollment certificate\n- `env`: Name of an environment variable containing a base64-encoded Ziti identity (alternative to `file`)\n- `service`: The name of the Ziti service to bind the controller API to\n\n### Using Environment Variables\n\nFor deployments where storing identity files on disk is not preferred, you can reference a base64-encoded\nidentity file from an environment variable. The environment variable should contain the base64-encoded contents\nof the identity JSON file.\n\nFor example, if an environment variable named `ZITI_CTRL_IDENTITY` contains a base64-encoded identity file:\n\n```text\n    bindPoints:\n      - interface: 127.0.0.1:18441\n        address: 127.0.0.1:18441\n      - identity:\n          env: ZITI_CTRL_IDENTITY\n          service: \"mgmt\"\n```\n\n### IPv6 Support\n\nBoth IPv4 and IPv6 addresses are supported for standard bind points. IPv6 addresses should be specified in bracket\nnotation with a port number:\n\n```text\n    bindPoints:\n      - interface: \"[::1]:18441\"\n        address: \"[::1]:18441\"\n      - identity:\n          file: \"/path/to/identity.json\"\n          service: \"mgmt\"\n```\n\n## CLI Enhancements for Identity-Based Connections\n\nThe `ziti edge login` command and REST client utilities have been enhanced to support identity-based connections\nthrough the Ziti overlay network.\n\n### New `--network-identity` Flag for `ziti edge login`\n\nThe `ziti edge login` command now includes a `--network-identity` flag that allows you to authenticate to a Ziti\ncontroller through the overlay network using a Ziti identity:\n\n```bash\nziti edge login https://ziti.mgmt.apis.local:1280 \\\n  --username myuser \\\n  --password mypass \\\n  --network-identity /path/to/identity.json\n```\n\nThis is useful when the controller is only accessible through the Ziti overlay network or when you want to ensure\nall communication to the controller flows through the overlay for security purposes.\n\n### Identity Resolution Order\n\nWhen establishing connections, identities are resolved in the following order:\n\n1. **Command-line flag**: The `--network-identity` flag takes precedence\n2. **Environment variable**: If `ZITI_CLI_NETWORK_ID` is set and contains a base64-encoded identity, it is used\n3. **Cached identity file**: If a network identity was saved from a previous login in the Ziti config directory, it may be used\n\nThis layered approach allows for flexibility in deployment scenarios:\n- Development: Use command-line flags for quick testing\n- Automation: Use environment variables in CI/CD pipelines\n- Production: Cache identities securely for repeated access\n\n#### Dialing Modes When Authenticating\n\nThe CLI supports two dialing modes:\n\n**Intercept-based Dialing (Default)**\nBy default, URLs are expected to leverage intercepts. Create a service with an appropriate intercept config and use\nthe intercept address when dialing. This is the standard mode for most use cases. For example, given a service with\nthe intercept `ziti.mgmt.apis.local`\n```bash\nziti edge login https://ziti.mgmt.apis.local:1280 \\\n  --username myuser \\\n  --password mypass \\\n  --network-identity /path/to/identity.json\n```\n\n**Identity-aware Dialing (Addressable Terminators)**\nTo support addressable terminators-based dialing, specify a user in the URL. This activates dial-by-identity\nfunctionality. The URL format should be `identity-to-dial@service-name-to-dial`. For example:\n```bash\nziti edge login https://my-identity@my-service:1280 \\\n  --username myuser \\\n  --password mypass \\\n  --network-identity /path/to/identity.json\n```\n\nIn this mode, the transport extracts the identity from the URL and uses it to establish a direct connection to\nthe specified service via the addressable terminator.\n\n\n## OIDC/JWT Token-based Enrollment\n\nOpenZiti now supports provisioning identities just-in-time through OIDC/JWT token enrollment. External identity \nproviders can be configured to allow identities to enroll using JWT tokens, with support for the resulting \nidentities to use certificate or token authentication.\n\n### External JWT Signer Configuration\n\nExternal JWT signers are configured via the Edge Management API to define enrollment behavior with the following new \nenrollment-specific properties:\n\n- **enrollToCertEnabled** - When enabled, identities can exchange a JWT token and a certificate signing request (CSR) \n        for a client certificate during enrollment. The certificate can then be used for standard certificate-based\n        authentication.\n\n- **enrollToTokenEnabled** - When enabled, identities can use a JWT token to enroll. The current token or future tokens\n        may be used for authentication.\n\n- **enrollNameClaimsSelector** - Specifies which JWT claim contains the identity name. Accepts a JSON pointer \n        (e.g., `/preferred_username`) or a simple property name (e.g., `preferred_username`, automatically converted to\n        `/preferred_username`). Defaults to `/sub` if not specified. The extracted value becomes the identity name in Ziti.\n\n- **enrollAttributeClaimsSelector** - Specifies which JWT claims to extract as identity attributes during enrollment.\n        Accepts a JSON pointer (e.g., `/roles`) or a simple property name (e.g., `roles`). Extracted attributes are \n        applied to the newly enrolled identity for use in authorization policies.\n\n- **enrollAuthPolicyId** - Specifies the authentication policy to apply to newly enrolled identities. This determines\n        what authentication methods are available for the identity post-enrollment.\n\nAdditionally the existing property named **claimsProperty** that specifies external id to match identities to:\n\n- now supports a JSON pointer (e.g., `/id`) or a simple property name (e.g., `id`)\n- is used to populate the `externalId` field of the identity\n\n### Enrollment Paths\n\n#### Certificate Enrollment (enrollToCertEnabled)\n\nWhen certificate enrollment is enabled, unauthenticated users can:\n\n1. Obtain a list of available IdPs from the public Edge Client API `GET /external-jwt-signers` endpoint, where \n   `enrollToCertEnabled` is set to `true`\n2. Obtain a JWT from the configured OIDC provider\n3. Generate a certificate signing request (CSR)\n4. Submit an enrollment request with the JWT and CSR\n5. Have their identity created in Ziti with attributes extracted from JWT claims\n6. Receive a signed client certificate for certificate-based authentication\n\n#### Token Enrollment (enrollToTokenEnabled)\n\nWhen token enrollment is enabled, unauthenticated users can:\n\n1. Obtain a list of available IdPs from the public Edge Client API `GET /external-jwt-signers` endpoint, where \n   `enrollToTokenEnabled` is set to `true`\n1. Obtain a JWT from the configured OIDC provider\n2. Submit an enrollment request with the JWT\n3. Have their identity created in Ziti with attributes extracted from JWT claims\n4. Receive a Ziti API token for token-based authentication\n\n### Edge Management API\n\nThe Edge Management API provides full CRUD operations for configuring external JWT signers:\n\n- `POST /external-jwt-signers` - Create a new external JWT signer with all configuration options\n- `GET /external-jwt-signers` - List all configured external JWT signers\n- `GET /external-jwt-signers/{id}` - Retrieve a specific signer configuration\n- `PUT /external-jwt-signers/{id}` - Update all fields of a signer\n- `PATCH /external-jwt-signers/{id}` - Partially update a signer\n- `DELETE /external-jwt-signers/{id}` - Delete a signer\n\n### Edge Client API\n\nThe Edge Client API exposes a reduced set of external JWT signer information for unauthenticated enrollment requests:\n\n- `GET /external-jwt-signers` - List available JWT signers with enrollment capabilities\n\nThe client API response includes the following fields for each signer:\n\n- `name` - Signer name\n- `externalAuthUrl` - URL where users obtain JWT tokens\n- `clientId` - OIDC client ID\n- `scopes` - Requested OIDC scopes\n- `openIdConfigurationUrl` - OIDC discovery endpoint\n- `audience` - Expected token audience\n- `targetToken` - Token type to use (ACCESS or ID)\n- **`enrollToCertEnabled`** - Flag indicating certificate enrollment is available\n- **`enrollToTokenEnabled`** - Flag indicating token enrollment is available\n\n### CLI Commands\n\n**Create an external JWT signer with enrollment options:**\n```\nziti edge controller create ext-jwt-signer <name> <issuer> \\\n  --jwks-endpoint <url> \\\n  --audience <audience> \\\n  --enroll-to-cert \\\n  --enroll-to-token=false \\\n  --enroll-name-claims-selector preferred_username \\\n  --enroll-attr-claims-selector roles \\\n  --enroll-auth-policy <policy-id-or-name>\n```\n\n**Update enrollment options on an existing signer:**\n```\nziti edge controller update ext-jwt-signer <name|id> \\\n  --enroll-to-cert \\\n  --enroll-auth-policy <policy-id-or-name>\n```\n\n**List external JWT signers:**\n```\nziti edge controller list ext-jwt-signers\n```\n\n## Clustering Performance Improvements\n\nIn previous releases, model updates were submitted to raft one at at time. This prevented \nraft from being efficient by allowing command batching. This release allows multiple \nmodel updates to be in-flight at the same time. \n\nNew Configuration Options\n\n1. Raft Apply Timeout (cluster.applyTimeout)\n\nLocation: Controller configuration file, under the cluster section\nType: Duration\nDefault: 5s\nDescription: Timeout for applying commands to the Raft distributed log. Commands that exceed this timeout will trigger adaptive rate limiter backoff.\n\nExample:\n```\ncluster:\n  applyTimeout: 10s\n```\n\n2. Cluster Rate Limiter Configuration (cluster.rateLimiter)\n\nA new adaptive rate limiter that controls the submission of commands to the Raft cluster. Unlike the existing command rate limiter, this specifically manages in-flight Raft operations with adaptive window sizing.\n\nConfiguration Structure:\n```\ncluster:\n  rateLimiter:\n    enabled: true\n    minSize: 5\n    maxSize: 250\n    timeout: 30s\n```\n\nSub-options:\n\n  - enabled (boolean)\n    - Default: true\n    - Description: Enable/disable adaptive rate limiting for Raft command submission\n  - minSize (integer)\n    - Default: 5\n    - Minimum: 1\n    - Description: Minimum window size for concurrent in-flight Raft operations\n  - maxSize (integer)\n    - Default: 250\n    - Description: Maximum window size for concurrent in-flight Raft operations. Must be >= minSize\n  - timeout (duration)\n    - Default: 30s\n    - Description: Time after which outstanding work is assumed to have failed if not marked completed\n\n3. Restart Self on Snapshot (cluster.restartSelfOnSnapshot)\n\nLocation: Controller configuration file, under cluster section\nType: Boolean\nDefault: false\nDescription: When true, the controller will automatically restart itself when restoring a snapshot to an initialized system. When false, the controller will exit with code 0, requiring external process management to restart it.\n\nExample:\n```\ncluster:\n    restartSelfOnSnapshot: true\n```\n\n### New Metrics\n\nThe adaptive rate limiter exposes three new metrics:\n\n  1. raft.rate_limiter.queue_size (gauge)\n    - Current number of operations queued/in-flight\n  2. raft.rate_limiter.work_timer (timer)\n    - Duration of rate-limited operations\n  3. raft.rate_limiter.window_size (gauge)\n    - Current adaptive window size\n\n## Rate Limiter Algorithm Improvements\n\nThe adaptive rate limiter tracker now uses a success rate metric to decide when to grow or shrink its\nconcurrency window, instead of using raw queue position. An exponentially decaying histogram tracks the\nratio of successes to backoffs. When the success rate exceeds a configurable threshold, the window is\ngrown by a multiplicative increase factor. When it falls below the threshold, the window is shrunk by a\nmultiplicative decrease factor. The check intervals for increase and decrease are independently configurable.\n\nThis approach produces smoother, more stable window adjustments under varying load, avoiding the\nover-aggressive shrinking that could occur when queue position alone was used as the signal.\n\nThis specific rate limiter implementation is used in three places:\n* **Controller TLS handshake rate limiting** - controls the rate of incoming TLS handshakes on the controller\n* **Raft command submission** - controls the rate of commands submitted to the Raft distributed log\n* **Router control channel rate limiting** - controls the rate of requests from the router to the controller\n\nNote: this work was done in advance of enabling the TLS handshake rate limiter by default. The TLS\nhandshake rate limiter is not yet enabled by default, but can be enabled via the `tls.rateLimiter`\nconfiguration section.\n\nNew configuration options (available under each `rateLimiter` section):\n\n  - successThreshold (float)\n    - Default: 0.9\n    - Description: Success rate threshold above which the window size will be increased and below which it will be decreased\n  - increaseFactor (float)\n    - Default: 1.02\n    - Description: Multiplier applied to the current window size when growing. Must be greater than 1\n  - decreaseFactor (float)\n    - Default: 0.9\n    - Description: Multiplier applied to the current window size when shrinking. Must be between 0 and 1\n  - increaseCheckInterval (integer)\n    - Default: 10\n    - Description: Number of successes between window size increase checks\n  - decreaseCheckInterval (integer)\n    - Default: 10\n    - Description: Number of backoffs between window size decrease checks\n\n## Background Processing for Identity Updates\n\nIdentity environment and authenticator updates that occur during authentication are now processed asynchronously in the background. \nThis prevents authentication requests from blocking when the system is under load, significantly improving resilience during thundering herd scenarios.\n\nWhen the background queue fills up, updates can be dropped as they will be refreshed on the next authentication attempt. \nThis allows the system to gracefully handle load spikes without impacting authentication performance.\n\nFor now, dropping entries when the queue fills will be disabled by default, but can be enabled, see below.\n\n### Configuration\n\nA new `command.background` configuration section controls the background processing behavior:\n\n```yaml\n  command:\n    background:\n      enabled: true           # Enable background processing (default: true)\n      queueSize: 1000        # Maximum queue size (default: 1000)\n      dropWhenFull: true     # Drop updates when queue is full (default: false)\n      delayThreshold: 50ms   # The threshold for how long updates are taking before starting to background updates\n```\n\nNote that the `commandRateLimiter` configuration section may instead be specified under `command` as `rateLimiter`.\n\nExample:\n\n```yaml\n  command:\n    background:\n      enabled: true\n      queueSize: 250\n      dropWhenFull: false\n      delayThreshold: 50ms\n    rateLimiter:\n      enabled:   true\n      maxQueued: 25\n```\n\nNote that if command rate limiter configuration is specified in both locations, the settings under `command` will take \nprecedence. The standalone `commandRateLimiter` section may be deprecated in the future.\n\n### Metrics\n\nWhen background processing is enabled, the following metrics are exposed:\n\n- command.background.queue_size - Current number of queued background tasks\n- command.background.worker_count - Current number of worker goroutines\n- command.background.busy_workers - Number of workers currently processing tasks\n- command.background.work_timer - Timer tracking background task execution (includes histogram, meter, and count)\n- command.background.dropped_entries - Count of dropped updates when queue is full (only when dropWhenFull is enabled)\n\n## New proxy.v1 Config Type\n\n*Originally released in 1.7.0*\n\nAdded support for dynamic service proxies with configurable binding and protocol options.\nThis allows Edge Routers and Tunnelers to create proxy endpoints that can forward traffic for Ziti services.\n\nThis differs from intercept.v1 in that intercept.v1 will intercept traffic on specified\nIP addresses or DNS entries to forward to a service using tproxy or tun interface,\ndepending on implementation.\n\nA proxy on the other hand will just start a regular TCP/UDP listener on the configured port,\nso traffic will have to be configured for that destination.\n\nExample proxy.v1 Configuration:\n\n```\n  {\n    \"port\": 8080,\n    \"protocols\": [\"tcp\"],\n    \"binding\": \"0.0.0.0\"\n  }\n```\n\nConfiguration Properties:\n  - port (required): Port number to listen on (1-65535)\n  - protocols (required): Array of supported protocols (tcp, udp)\n  - binding (optional): Interface to bind to. For the ER/T defaults to the configured lanIF config property.\n\nThis config type is currently supported by the ER/T when running in either proxy or tproxy mode.\n\n## Alert Events (BETA)\n\n*Originally released in 1.7.0*\n\nA new alert event type has been added to allow Ziti components to emit alerts for issues that network operators can address.\nAlert events are generated when components encounter problems such as service configuration errors or resource\navailability issues.\n\nAlert events include:\n  - Alert source type and ID (currently supports routers, with controller and SDK support planned for future releases)\n  - Severity level (currently supports error, with info and warning planned for future releases)\n  - Alert message and supporting details\n  - Related entities (router, identity, service, etc.) associated with the alert\n\nExample alert event when a router cannot bind a configured network interface:\n\n```\n  {\n    \"namespace\": \"alert\",\n    \"event_src_id\": \"ctrl1\",\n    \"timestamp\": \"2021-11-08T14:45:45.785561479-05:00\",\n    \"alert_source_type\": \"router\",\n    \"alert_source_id\": \"DJFljCCoLs\",\n    \"severity\": \"error\",\n    \"message\": \"error starting proxy listener for service 'test'\",\n    \"details\": [\n      \"unable to bind eth0, no address\"\n    ],\n    \"related_entities\": {\n      \"router\": \"DJFljCCoLs\",\n      \"identity\": \"DJFljCCoLs\",\n      \"service\": \"3DPjxybDvXlo878CB0X2Zs\"\n    }\n  }\n```\n\nAlert events can be consumed through the standard event system and logged to configured event handlers for monitoring and alerting purposes.\n\nThese events are currently in Beta, as the format is still subject to change. Once they've been in use in production for a while\nand proven useful, they will be marked as stable.\n\n## Azure Service Bus Event Sink\n\n*Originally released in 1.7.0. Contributed by @ffaraone.*\n\nAdds support for streaming controller events to Azure Service Bus.\nThe new logger enables real-time event streaming from the OpenZiti controller to Azure Service Bus\nqueues or topics, providing integration with Azure-based monitoring and analytics systems.\n\nTo enable the Azure Service Bus event logger, add configuration to the controller config file under the events section:\n\n```\n  events:\n    serviceBusLogger:\n      subscriptions:\n        - type: circuit\n        - type: session\n        - type: metrics\n          sourceFilter: .*\n          metricFilter: .*\n        # Add other event types as needed\n      handler:\n        type: servicebus\n        format: json\n        connectionString: \"Endpoint=sb://your-namespace.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=your-key\"\n        topic: \"ziti-events\"          # Use 'topic' for Service Bus topic\n        # queue: \"ziti-events-queue\"  # Or use 'queue' for Service Bus queue\n        bufferSize: 100                # Optional, defaults to 50\n```\n\n- Required configuration:\n    - format: Event format, currently supports only json\n    - connectionString: Azure Service Bus connection string\n    - Either topic or queue: Destination name (mutually exclusive)\n\n- Optional configuration:\n    - bufferSize: Internal message buffer size (default: 50)\n\n## OIDC is now enabled by default\n\nThe controller now automatically adds the `edge-oidc` API binding to any web listener that hosts\nthe `edge-client` API, even when `edge-oidc` is not explicitly listed in the `web` section of the\nconfiguration. This means OIDC-based authentication is available out of the box without requiring\nchanges to existing controller configurations.\n\n### Where OIDC binds\n\nThe `edge-oidc` binding is added to the same web listener(s) as `edge-client`. If `edge-client` is\nhosted on `127.0.0.1:1280`, the OIDC endpoints will be available on that same address under the\n`/oidc` path (e.g. `https://127.0.0.1:1280/oidc/.well-known/openid-configuration`).\n\nThe controller capabilities returned by `GET /version` will include `OIDC_AUTH` and the\n`edge-oidc` entry will be present in `apiVersions` when OIDC is active.\n\n### Opting out\n\nIf OIDC should not be enabled automatically, set `disableOidcAutoBinding: true` under `edge.api`:\n\n```yaml\nedge:\n  api:\n    address: 127.0.0.1:1280\n    sessionTimeout: 30m\n    disableOidcAutoBinding: true\n```\n\nWhen `disableOidcAutoBinding` is `true`, OIDC will only be active if `edge-oidc` is explicitly\nlisted as a binding in the `web` section. \n\nWhen OIDC is not enabled, all (SDK) clients will revert to using legacy authentication.\nLegacy authentication does not support multiple controllers or HA in general. Clients will treat\ntheir controller as if it is a single controller instance and will function as if no other controllers\nexist.\n\n\n## WWW-Authenticate Headers\n\nThe controller's Edge APIs now include `WWW-Authenticate` headers on `401 Unauthorized` responses,\nper issuer: https://github.com/openziti/ziti/issues/3356. These headers provide insight into why\na token was rejected. The main benefit of these headers is to convey information for JWT backed\nAPI Sessions and API Session authentication where a token may not have been provided (missing),\nis used beyond its expiration date (expired), or the token has become invalid for any other\nreason (invalid).\n\n`www-authenticate` headers are provided as a single header instance with multiple challenge values\nseparate by commas.\n\n### No Credentials Provided\n\nWhen a request hits a protected endpoint without any credentials, a single `WWW-Authenticate` header\nis returned listing both accepted auth schemes as comma-separated challenges:\n\n```\nWWW-Authenticate: zt-session realm=\"zt-session\" error=\"missing\" error_description=\"no matching token was provided\",Bearer realm=\"openziti-oidc\" error=\"missing\" error_description=\"no matching token was provided\"\n```\n\n### Token Errors\n\nWhen a token is present but cannot be accepted, the header identifies the scheme and what went wrong:\n\n```\nWWW-Authenticate: Bearer realm=\"openziti-oidc\" error=\"expired\" error_description=\"token expired\"\nWWW-Authenticate: Bearer realm=\"openziti-oidc\" error=\"invalid\" error_description=\"token is invalid\"\nWWW-Authenticate: zt-session realm=\"zt-session\" error=\"invalid\" error_description=\"token is invalid\"\n```\n\n### OIDC External JWT — Primary Authentication\n\nWhen an auth policy requires an external JWT signer for primary authentication (e.g., a PKCE flow\nbacked by an ext-jwt signer), the header identifies which signers are accepted and what went wrong.\nMultiple accepted signers are pipe-delimited in the `id` and `issuer` parameters:\n\n```\nWWW-Authenticate: Bearer realm=\"openziti-primary-ext-jwt\" error=\"missing\" error_description=\"no matching token was provided\" id=\"signer-id-1|signer-id-2\" issuer=\"https://issuer1.example.com|https://issuer2.example.com\"\n```\n\nThe `error` value follows the same `missing`/`expired`/`invalid` pattern as standard bearer token errors.\n\n### OIDC External JWT — Secondary / MFA Authentication\n\nWhen an auth policy requires an external JWT signer as a secondary factor (step-up after primary\nauth succeeds), the header identifies the single required signer. The `error` value follows the\nsame `missing`/`expired`/`invalid` pattern:\n\n```\nWWW-Authenticate: Bearer realm=\"openziti-secondary-ext-jwt\" error=\"missing\" error_description=\"no matching token was provided\" id=\"<signer-id>\" issuer=\"<issuer>\"\n```\n\n### Anonymous Endpoints\n\nUnauthenticated endpoints such as version information do not return `WWW-Authenticate` headers.\n\n## HA Preferred Leaders\n\nControllers can be marked as a preferred leader. \n\n**Example Config**\n```yaml\ncluster:\n  dataDir: /home/{{ .Model.MustVariable \"credentials.ssh.username\" }}/fablab/ctrldata\n  preferredLeader: true\n```\n\nIf a controller that is not marked preferredLeader becomes a preferredLeader, it will check \nif there's a node available that is marked as preferred. If there is one, or one later\njoins the cluster, the non-preferred node will attempt to transfer leadership to the \nnode that is marked as preferred.\n\n## Expanded Dynamic Cost Range\n\nThe dynamic cost range for smart routing has been expanded beyond the previous 64K limit. Under high\nload, terminators could saturate the cost space, making dynamic cost values meaningless for routing\ndecisions and leading to uneven load distribution. The expanded range allows for more granular cost\ndifferentiation even under heavy load.\n\n## Circuit ID and Error in Dial Failures\n\nDial failures now return the circuit ID and, when available, the error that caused the circuit to fail.\nPreviously, the circuit ID was only returned on successful dials. Note that SDKs will need to be\nupdated to surface the circuit id when a dial failure happens.\n\n## Multi-Underlay Control Channels\n\nRouter-to-controller control channels now support multiple underlays with priority-based message routing.\nThis allows time-sensitive control messages (heartbeats, routing, circuit requests) to be separated from\noperational data (metrics, inspections) across dedicated TCP connections, preventing bulk operations from\ndelaying user-affecting control plane traffic. This feature does not yet allow specifying multiple \nnetwork interfaces to use, to load balance data across.\n\n## Dialing Identity Forwarded to Hosting SDK\n\nThe identity ID and name of the dialing client are now forwarded to the hosting SDK when a circuit is\nestablished. This allows hosting applications to identify which identity initiated the connection,\nenabling identity-aware request handling on the server side. This will require SDK updates to add this\nto the API for hosting applications.\n\n## Controller-Initiated Control Channel Dials (BETA)\n\nControllers can now dial routers to establish control channels. Previously, routers were solely\nresponsible for dialing controllers. This feature is designed for deployments where one or more\ncontrollers are in a private network that routers cannot reach, but the controllers can dial out.\nA common scenario is an HA cluster where some controllers are publicly reachable and some are in\na private network. External routers connect to the public controllers normally, and the private\ncontrollers dial out to the routers.\n\n### Router Configuration\n\nRouters can configure one or more control channel listeners. Each listener specifies a bind address,\nan advertise address (reported to the controller), and optional groups for matching.\n\n```yaml\nctrl:\n  listeners:\n    - bind: tls://0.0.0.0:6262\n      advertise: tls://router.example.com:6262\n      groups:\n        - default\n```\n\nThe router is the authoritative source of ctrl channel listener information, similar to link\nlisteners. When a router connects to any controller, it reports its configured `ctrlChanListeners`\nand the controller data model is updated automatically. This means that in most deployments —\nwhere the router can reach at least one controller — no manual configuration of listener addresses\nis needed on the controller side.\n\nThe `ctrlChanListeners` field can also be set via the CLI for cases where a router cannot reach\nany controller and thus cannot initialize the data model itself:\n\n```bash\nziti edge update edge-router myRouter --bootstrap-ctrl-chan-listener 'tls://router.example.com:6262=group1,group2'\n```\n\nGroups default to `[\"default\"]` if not specified.\n\n### Controller Configuration\n\nThe controller dialer is disabled by default and must be explicitly enabled. When enabled, the\ncontroller will dial routers that have control channel listeners configured and are not already\nconnected.\n\n```yaml\nctrl:\n  dialer:\n    enabled: true\n    groups:\n      - default\n    dialDelay: 30s\n    minRetryInterval: 1s\n    maxRetryInterval: 5m\n    retryBackoffFactor: 1.5\n    fastFailureWindow: 5s\n    queueSize: 32\n    maxWorkers: 10\n```\n\n- `enabled` - Enables the controller dialer (default: `false`)\n- `groups` - List of groups to match against router listener groups (default: `[\"default\"]`)\n- `dialDelay` - Delay before the controller starts dialing after boot (default: `30s`)\n- `minRetryInterval` - Minimum backoff delay between dial retries (default: `1s`)\n- `maxRetryInterval` - Maximum backoff delay between dial retries (default: `5m`)\n- `retryBackoffFactor` - Multiplier applied to the retry delay on each failure, jittered +/- 0.5 (default: `1.5`)\n- `fastFailureWindow` - If a connection drops within this window after connecting, backoff continues rather than resetting (default: `5s`)\n- `queueSize` - Maximum number of pending dial jobs in the worker pool queue (default: `32`)\n- `maxWorkers` - Maximum number of concurrent dial workers (default: `10`)\n\nThe controller will only dial routers whose listener groups overlap with the controller's configured\ngroups. When a router advertises multiple ctrl channel listener addresses, the dialer rotates through\nthem on each failure so that an unreachable address does not block attempts to the others.\n\n### Metrics\n\nThe controller dialer worker pool exposes the following metrics under the `ctrl_channel.dialer` prefix:\n\n- `ctrl_channel.dialer.queue_size` - Current number of pending dial jobs in the queue\n- `ctrl_channel.dialer.worker_count` - Current number of dial worker goroutines\n- `ctrl_channel.dialer.busy_workers` - Number of workers currently executing a dial\n- `ctrl_channel.dialer.work_timer` - Timer tracking the duration of each dial attempt\n\n## SDK Inspection Support\n\nNew CLI commands have been added for inspecting SDK state, useful for diagnosing terminator and\nconnectivity issues.\n\n**Note:** SDK inspection requires SDK support. Currently only the Go SDK supports inspection,\nas of version 1.5.0. Other SDKs will need to add support before these commands can be used\nwith them.\n\n### `ziti fabric inspect sdk`\n\nRetrieves SDK context inspection data from identities connected to routers.\n\n```\nziti fabric inspect sdk <target-selector> <identity-id>\n```\n\nThe `<target-selector>` is a regex matching router IDs (use `.*` for all routers). The\n`<identity-id>` is the identity whose SDK context you want to inspect. The command returns\ndetailed state from the connected SDK instance, including active services, terminators, and\nconnection status.\n\n### `ziti agent tunnel dump-sdk`\n\nDumps SDK context information from a running `ziti tunnel` process via the IPC agent.\n\n```\nziti agent tunnel dump-sdk\n```\n\nReturns JSON-formatted inspection data for all SDK contexts registered in the tunnel process,\nincluding service listeners, connections, and terminator state.\n\n## Current Beta Features\n\n* Basic Permission System\n* Alert Events\n* Controller-Initiated Control Channel Dials\n\n## Revocation System Improvements\n\nWhen a session is refreshed, the old refresh token's revocation is no longer created\nsynchronously through raft. Instead, revocations are queued in memory and flushed in\nbatches on a configurable interval. This removes the database and raft as a bottleneck\non token refreshes. If the old token is close to expiring, the revocation is skipped\nentirely.\n\nNew configuration tunables under `edge.oidc`:\n\n| Key | Default | Description |\n|-----|---------|-------------|\n| `revocationMinTokenLifetime` | unset | Skip revocation if the old token expires within this duration (must be < 50% of `refreshTokenDuration`) |\n| `revocationBucketInterval` | `1m` | Bucket window for batching revocations before flushing through raft |\n| `revocationBucketMaxSize` | `200` | Max revocations per raft entry |\n| `revocationMaxQueued` | `25000` | Max revocations queued in memory before dropping |\n| `revocationEnforcerFrequency` | `1m` | How often expired revocations are purged (leader only) |\n\n## Component Updates and Bug Fixes\n\n* github.com/openziti/agent: [v1.0.31 -> v1.0.33](https://github.com/openziti/agent/compare/v1.0.31...v1.0.33)\n* github.com/openziti/channel/v4: [v4.2.28 -> v4.3.9](https://github.com/openziti/channel/compare/v4.2.28...v4.3.9)\n    * [Issue #235](https://github.com/openziti/channel/issues/235) - Bump allowed hello message headers size to 16k from 4k\n    * [Issue #228](https://github.com/openziti/channel/issues/228) - Ensure that Underlay never return nil on MultiChannel\n    * [Issue #226](https://github.com/openziti/channel/issues/226) - Allow specifying a minimum number of underlays for a channel, regardless of underlay type\n    * [Issue #225](https://github.com/openziti/channel/issues/225) - Add ChannelCreated to the UnderlayHandler API to allow handlers to be initialized with the channel before binding\n    * [Issue #224](https://github.com/openziti/channel/issues/224) - Update the underlay dispatcher to allow unknown underlay types to fall through to the default\n    * [Issue #222](https://github.com/openziti/channel/issues/222) - Allow injecting the underlay type into messages\n\n* github.com/openziti/edge-api: [v0.26.47 -> v0.27.5](https://github.com/openziti/edge-api/compare/v0.26.47...v0.27.5)\n    * [Issue #175](https://github.com/openziti/edge-api/issues/175) - ctrlChanListeners should have x-omit-empty: false attribute\n    * [Issue #170](https://github.com/openziti/edge-api/issues/170) - Add preferredLeader flag to controllers\n    * [Issue #167](https://github.com/openziti/edge-api/issues/167) - Add ctrlChanListeners to router types\n    * [Issue #164](https://github.com/openziti/edge-api/issues/164) - Add permissions list to identity\n\n* github.com/openziti/foundation/v2: [v2.0.72 -> v2.0.90](https://github.com/openziti/foundation/compare/v2.0.72...v2.0.90)\n    * [Issue #472](https://github.com/openziti/foundation/issues/472) - Add support for multi-bit set/get to AtomicBitSet\n    * [Issue #464](https://github.com/openziti/foundation/issues/464) - Add support for -pre in versions\n    * [Issue #455](https://github.com/openziti/foundation/issues/455) - Correctly close goroutine pool when external close is signaled\n    * [Issue #452](https://github.com/openziti/foundation/issues/452) - Goroutine pool with a min worker count of 1 can drop to 0 workers due to race condition\n\n* github.com/openziti/identity: [v1.0.111 -> v1.0.128](https://github.com/openziti/identity/compare/v1.0.111...v1.0.128)\n    * [Issue #68](https://github.com/openziti/identity/issues/68) - Shutdown file watcher when stopping identity watcher\n\n* github.com/openziti/metrics: [v1.4.2 -> v1.4.5](https://github.com/openziti/metrics/compare/v1.4.2...v1.4.5)\n    * [Issue #58](https://github.com/openziti/metrics/issues/58) - Add GaugeFloat64 support\n    * [Issue #56](https://github.com/openziti/metrics/issues/56) - underlying resources of reference counted meters are not cleaned up when reference count hits zero\n\n* github.com/openziti/runzmd: [v1.0.80 -> v1.0.90](https://github.com/openziti/runzmd/compare/v1.0.80...v1.0.90)\n* github.com/openziti/sdk-golang: [v1.2.3 -> v1.5.3](https://github.com/openziti/sdk-golang/compare/v1.2.3...v1.5.3)\n    * [Issue #887](https://github.com/openziti/sdk-golang/issues/887) - Fix listener manager cleanup\n    * [Issue #886](https://github.com/openziti/sdk-golang/issues/886) - When controller is busy during service refresh, backoff and retry instead of falling back to full refresh\n    * [Issue #885](https://github.com/openziti/sdk-golang/issues/885) - Only compare relevant service fields when looking for changes\n    * [Issue #884](https://github.com/openziti/sdk-golang/issues/884) - Add deadline for bind establishment\n    * [Issue #883](https://github.com/openziti/sdk-golang/issues/883) - Router level listener can be left open if multi-listener closes during listener establishment\n    * [Issue #877](https://github.com/openziti/sdk-golang/issues/877) - Handle differences in xgress eof/end-of-circuit handling by adding a capabilities exchange\n    * [Issue #832](https://github.com/openziti/sdk-golang/issues/832) - Fuzz session refresh timers\n    * [Issue #879](https://github.com/openziti/sdk-golang/issues/879) - Return the connId in inspect response\n    * [Issue #878](https://github.com/openziti/sdk-golang/issues/878) - Fix responses from rx goroutines\n    * [Issue #874](https://github.com/openziti/sdk-golang/issues/874) - Add inspect support at the context level\n    * [Issue #871](https://github.com/openziti/sdk-golang/issues/871) - Make SDK better at sticking to MaxTerminator terminators\n    * [Issue #708](https://github.com/openziti/sdk-golang/issues/708) - Support for Go's built-in context in Dial methods\n    * [Issue #860](https://github.com/openziti/sdk-golang/issues/860) - Make the dialing identity's id and name available on dialed connections\n    * [Issue #857](https://github.com/openziti/sdk-golang/issues/857) - Use new error code and retry hints to correctly react to terminator errors\n    * [Issue #847](https://github.com/openziti/sdk-golang/issues/847) - Ensure the initial version check succeeds, to ensure we don't legacy sessions on ha or oidc-enabled controllers\n    * [Issue #824](https://github.com/openziti/sdk-golang/pull/824) - release notes and hard errors on no TOTP handler breaks partial auth events\n    * [Issue #818](https://github.com/openziti/sdk-golang/issues/818) - Full re-auth should not clear services list, as that breaks the on-change logic\n    * [Issue #817](https://github.com/openziti/sdk-golang/issues/817) - goroutines can get stuck when iterating over randomized HA controller list\n    * [Issue #736](https://github.com/openziti/sdk-golang/issues/736) - Migrate from github.com/mailru/easyjson\n    * [Issue #813](https://github.com/openziti/sdk-golang/issues/813) - SDK doesn't stop close listener when it detects that a service being hosted gets deleted\n    * [Issue #811](https://github.com/openziti/sdk-golang/issues/811) - Credentials are lost when explicitly set\n    * [Issue #807](https://github.com/openziti/sdk-golang/issues/807) - Don't send close from rxer to avoid blocking\n    * [Issue #800](https://github.com/openziti/sdk-golang/issues/800) - Tidy create service session logging\n\n* github.com/openziti/secretstream: [v0.1.39 -> v0.1.49](https://github.com/openziti/secretstream/compare/v0.1.39...v0.1.49)\n* github.com/openziti/storage: [v0.4.26 -> v0.4.39](https://github.com/openziti/storage/compare/v0.4.26...v0.4.39)\n    * [Issue #122](https://github.com/openziti/storage/issues/122) - StringFuncNode has incorrect nil check, allowing panic\n    * [Issue #120](https://github.com/openziti/storage/issues/120) - Change post tx commit constraint handling order\n    * [Issue #119](https://github.com/openziti/storage/issues/119) - Add ContextDecorator API\n\n* github.com/openziti/transport/v2: [v2.0.188 -> v2.0.215](https://github.com/openziti/transport/compare/v2.0.188...v2.0.215)\n    * [Issue #31](https://github.com/openziti/transport/issues/31) - ipv6 Transport Address Parsing\n    * [Issue #149](https://github.com/openziti/transport/issues/149) - Archive transwarp code\n\n* github.com/openziti/xweb/v3: [v2.3.4 -> v3.0.4](https://github.com/openziti/xweb/compare/v2.3.4...v3.0.4)\n    * [Issue #32](https://github.com/openziti/xweb/issues/32) - watched identities sometimes don't reload when changed\n\n* github.com/openziti/go-term-markdown: v1.0.1 (new)\n* github.com/openziti/ziti/v2: [v1.6.8 -> v2.0.0](https://github.com/openziti/ziti/compare/v1.6.8...v2.0.0)\n    * [Issue #3681](https://github.com/openziti/ziti/issues/3681) - coalesce OIDC JWT revocations to reduce controller write pressure\n    * [Issue #3683](https://github.com/openziti/ziti/issues/3683) - add fablab test for testing flow control changes over a longer term\n    * [Issue #3673](https://github.com/openziti/ziti/issues/3673) - revocation build-up in db and rdm\n    * [Issue #3674](https://github.com/openziti/ziti/issues/3674) - Update to Go 1.26\n    * [Issue #3496](https://github.com/openziti/ziti/issues/3496) - MFA TOTP Enrollment During OIDC Authentication Does Not Work\n    * [Issue #3609](https://github.com/openziti/ziti/issues/3609) - Stabilize terminator creation test for 2.0\n    * [Issue #3648](https://github.com/openziti/ziti/issues/3648) - tunnel: myCopy logs router ID as circuitId, causing misleading debug output\n    * [Issue #3658](https://github.com/openziti/ziti/issues/3658) - Raft cluster join fails with \"hello message too big\" when using long hostnames\n    * [Issue #3626](https://github.com/openziti/ziti/issues/3626) - controller: overlay bind point produces malformed URL in /versions apiBaseUrls\n    * [Issue #3635](https://github.com/openziti/ziti/issues/3635) - Allow controllers to dial routers to support more topologies\n    * [Issue #3607](https://github.com/openziti/ziti/issues/3607) - linux installer not upgradable from v1\n    * [Issue #3650](https://github.com/openziti/ziti/issues/3650) - Reroute doesn't proactively clean up orphaned route entries\n    * [Issue #3571](https://github.com/openziti/ziti/issues/3571) - Ensure 2.0 backwards compatibility with 1.6 and 1.5 using the smoketest\n    * [Issue #3636](https://github.com/openziti/ziti/issues/3636) - Adaptive rate limiter should use success rate rather than queue position\n    * [Issue #3600](https://github.com/openziti/ziti/issues/3600) - Add a preferredLeader flag, allow selected nodes to be preferred for raft leader, if they're available\n    * [Issue #3642](https://github.com/openziti/ziti/issues/3642) - Use xgress_common.Connection type for xgress_transport and xgress_proxy\n    * [Issue #3597](https://github.com/openziti/ziti/issues/3597) - Enable OIDC API by default\n    * [Issue #3624](https://github.com/openziti/ziti/issues/3624) - Multi-underlay control channel doesn't correctly handle lack of group secret on non-grouped underlays\n    * [Issue #3613](https://github.com/openziti/ziti/issues/3613) - Initializing cluster from existing db using `db:` config settings results in panic\n    * [Issue #3620](https://github.com/openziti/ziti/issues/3620) - Controller control channel heartbeats config parsing was erroneously nested under options parsing\n    * [Issue #3619](https://github.com/openziti/ziti/issues/3619) - Router connect events full sync interval was using min/max values meant for the batch interval\n    * [Issue #3618](https://github.com/openziti/ziti/issues/3618) - Router interfaceDiscovery.minReportInterval value was being set to checkInterval\n    * [Issue #3617](https://github.com/openziti/ziti/issues/3617) - Transit router disabled flag not passed through raft command structure\n    * [Issue #3333](https://github.com/openziti/ziti/issues/3333) - Updb user-lockout triggered by successful login attempts\n    * [Issue #3599](https://github.com/openziti/ziti/issues/3599) - Add gap detection and handling to router data model\n    * [Issue #3356](https://github.com/openziti/ziti/issues/3356) - Add WWW-Authenticate Headers\n    * [Issue #3074](https://github.com/openziti/ziti/issues/3074) - Dynamic cost range is too limited\n    * [Issue #3558](https://github.com/openziti/ziti/issues/3558) - terminator cost increased on egress dial success, not on circuit completion\n    * [Issue #3556](https://github.com/openziti/ziti/issues/3556) - global circuit costs not cleared when terminator is deleted\n    * [Issue #3557](https://github.com/openziti/ziti/issues/3557) - costing calculation for the weighted terminator selection strategy  is incorrect\n    * [Issue #2512](https://github.com/openziti/ziti/issues/2512) - Return circuit ID and error in dial failures\n    * [Issue #3569](https://github.com/openziti/ziti/issues/3569) - Version 2.0+ routers should not connect to controllers which do not support JWT formatted legacy sessions\n    * [Issue #3565](https://github.com/openziti/ziti/issues/3565) - Link dialer save 'is first conn' true, so all dials claim to be first, causing potential race condition\n    * [Issue #3550](https://github.com/openziti/ziti/issues/3550) - Support multi-underlay control channels\n    * [Issue #3535](https://github.com/openziti/ziti/issues/3535) - Remove the legacy xgress_edge_tunnel implementation\n    * [Issue #3547](https://github.com/openziti/ziti/issues/3547) - Add support for sending the dialing identity id and name to the hosting sdk\n    * [Issue #3541](https://github.com/openziti/ziti/issues/3541) - Remove option to disable the router data model in the controller\n    * [Issue #3540](https://github.com/openziti/ziti/issues/3540) - Handle UDP difference between proxy and tproxy implementations\n    * [Issue #3527](https://github.com/openziti/ziti/issues/3527) - ER/T UDP tunnels keep closed connections for 30s, preventing potential new good connections in that time\n    * [Issue #3526](https://github.com/openziti/ziti/issues/3526) - ER/T half-close logic is incorrect\n    * [Issue #3524](https://github.com/openziti/ziti/issues/3524) - Provide more error context to SDKs for terminator errors\n    * [Issue #3509](https://github.com/openziti/ziti/issues/3509) - Enforce policy on the router for oidc sessions, by closing open circuits and terminators when service access is lost\n    * [Issue #3531](https://github.com/openziti/ziti/issues/3531) - Remove created/updated/deleted terminator events. Obsoleted by entity change events.\n    * [Issue #3532](https://github.com/openziti/ziti/issues/3532) - Removed deprecated create identity <type> subcommands\n    * [Issue #3521](https://github.com/openziti/ziti/issues/3521) - Cleanup CLI to remove calls to os.Exit to be embed friendlier\n    * [Issue #3516](https://github.com/openziti/ziti/issues/3516) - Remove support for create terminator v1\n    * [Issue #3512](https://github.com/openziti/ziti/issues/3512) - Remove legacy link management code from the controller\n    * [Issue #3511](https://github.com/openziti/ziti/issues/3511) - router proxy mode fails to resolve interface if binding is 0.0.0.0\n    * [Issue #3503](https://github.com/openziti/ziti/issues/3503) - Allow routers to request current cluster membership information\n    * [Issue #3501](https://github.com/openziti/ziti/issues/3501) - Get cluster membership information from raft directly, rather than trying to cache it in the DB\n    * [Issue #3500](https://github.com/openziti/ziti/issues/3500) - Set a router data model timeline when initializing a new HA setup, rather than letting it stay blank\n    * [Issue #3504](https://github.com/openziti/ziti/issues/3504) - Reduce router data model full state updates\n    * [Issue #3492](https://github.com/openziti/ziti/pull/3492) - Bump openziti/ziti-console-assets from 3.12.9 to 4.0.0 in /dist/docker-images/ziti-controller in the all group\n    * [Issue #3484](https://github.com/openziti/ziti/issues/3484) - router ctrl channel handler for handling cluster changes has an initialization race condition\n    * [Issue #3477](https://github.com/openziti/ziti/issues/3477) - Optionally enable model changes triggered by login to be non-blocking and to be droppable if the system is under load\n    * [Issue #3473](https://github.com/openziti/ziti/issues/3473) - Enable tls handshake rate limiter by default and tweak default values.\n    * [Issue #3471](https://github.com/openziti/ziti/issues/3471) - Go tunneler is ignoring host config MaxConnections\n    * [Issue #3469](https://github.com/openziti/ziti/issues/3469) - Only send model updates on resubscribe if the RDM index has advanced\n    * [Issue #2573](https://github.com/openziti/ziti/issues/2573) - An edge router in a tight restart loop causes a resource leak on routers to which it connects.\n    * [Issue #3430](https://github.com/openziti/ziti/issues/3430) - Add permissions list to identity\n    * [Issue #2109](https://github.com/openziti/ziti/issues/2109) - Add Edge Management Read Only Capability\n    * [Issue #3435](https://github.com/openziti/ziti/issues/3435) - Add edge management API permissions by entity type and action\n    * [Issue #3441](https://github.com/openziti/ziti/issues/3441) - Update router connection tracker to interrogate active connections\n    * [Issue #3451](https://github.com/openziti/ziti/issues/3451) - ci - compare only stable releases when promoting\n    * [Issue #3437](https://github.com/openziti/ziti/issues/3437) - SDK OIDC token updates to routers should return an error if invalid\n    * [Issue #3348](https://github.com/openziti/ziti/issues/3348) - Unable to clear/reset the \"tags\" property on an entity to an empty object\n    * [Issue #3452](https://github.com/openziti/ziti/issues/3452) - `ziti agent cluster add` has bad behavior if the add address doesn't match the advertise address\n    * [Issue #3410](https://github.com/openziti/ziti/issues/3410) - Consolidate fabric REST API code with edge management and edge client code\n    * [Issue #3425](https://github.com/openziti/ziti/issues/3425) - RDM not properly responding to tunneler enabled flag changes\n    * [Issue #3420](https://github.com/openziti/ziti/issues/3420) - The terminator id cache uses the same id for all terminators in a host.v2 config, resulting in a single terminator\n    * [Issue #3419](https://github.com/openziti/ziti/issues/3419) - When using the router data model, precedence specified on the per-service identity mapping are incorrectly interpreted\n    * [Issue #3318](https://github.com/openziti/ziti/issues/3318) - Terminator creation seems to slow exponentially as the number of terminators rises from 10k to 20k to 40k\n    * [Issue #3407](https://github.com/openziti/ziti/issues/3407) - The CLI doesn't properly pass JWT authentication information to websocket endpoints\n    * [Issue #3359](https://github.com/openziti/ziti/issues/3359) - Ensure router data model subscriptions have reasonable performance and will scale\n    * [Issue #3354](https://github.com/openziti/ziti/issues/3354) - SDK/ENV Info from SDKs is not distributed in HA\n    * [Issue #3381](https://github.com/openziti/ziti/issues/3381) - the fabric service REST apis are missing the maxIdleTime property\n    * [Issue #3382](https://github.com/openziti/ziti/issues/3382) - Legacy service sessions generated pre-1.7.x are incompatible with v1.7.+ and need to be cleared\n    * [Issue #3339](https://github.com/openziti/ziti/issues/3339) - get router ctrl.endpoint from ctrls claim in JWT\n    * [Issue #3378](https://github.com/openziti/ziti/issues/3378) - login with file stopped working\n    * [Issue #3349](https://github.com/openziti/ziti/issues/3349) - UPDB OIDC login returns wrong content type\n    * [Issue #2324](https://github.com/openziti/ziti/issues/2324) - Add Ext-JWT/OIDC enrollment\n    * [Issue #3346](https://github.com/openziti/ziti/issues/3346) - Fix confusing attempt logging\n    * [Issue #3337](https://github.com/openziti/ziti/issues/3337) - Router reports \"no xgress edge forwarder for circuit\"\n    * [Issue #3345](https://github.com/openziti/ziti/issues/3345) - Clean up connect events tests and remove global XG registry\n    * [Issue #3264](https://github.com/openziti/ziti/issues/3264) - Allow routers to generate alert events in cases of service misconfiguration\n    * [Issue #3321](https://github.com/openziti/ziti/issues/3321) - Health Check API missing base path on discovery endpoint\n    * [Issue #3323](https://github.com/openziti/ziti/issues/3323) - router/tunnel static services fail to bind unless new param protocol is defined\n    * [Issue #3309](https://github.com/openziti/ziti/issues/3309) - Detect link connections meant for another router\n    * [Issue #3286](https://github.com/openziti/ziti/issues/3286) - edge-api binding doesn't have the correct path on discovery endpoints\n    * [Issue #3297](https://github.com/openziti/ziti/issues/3297) - stop promoting hotfixes downstream\n    * [Issue #3295](https://github.com/openziti/ziti/issues/3295) - make ziti tunnel service:port pairs optional\n    * [Issue #3291](https://github.com/openziti/ziti/issues/3291) - replace decommissioned bitnami/kubectl\n    * [Issue #3277](https://github.com/openziti/ziti/issues/3277) - Router can deadlock on closing a connection if the incoming data channel is full\n    * [Issue #3269](https://github.com/openziti/ziti/issues/3269) - Add host-interfaces config type\n    * [Issue #3258](https://github.com/openziti/ziti/issues/3258) - Add config type proxy.v1 so proxies can be defined dynamically for the ER/T\n    * [Issue #3259](https://github.com/openziti/ziti/issues/3259) - Interfaces config type not added due to wrong name\n    * [Issue #3265](https://github.com/openziti/ziti/issues/3265) - Forwarding errors should log at debug, since they are usual part of circuit teardown\n    * [Issue #3261](https://github.com/openziti/ziti/issues/3261) - ER/T dialed xgress connections may only half-close when peer is fully closed\n    * [Issue #3207](https://github.com/openziti/ziti/issues/3207) - Allow router embedders to customize config before start\n\n\n","mentions_count":1},{"url":"https://api.github.com/repos/openziti/ziti/releases/296863126","assets_url":"https://api.github.com/repos/openziti/ziti/releases/296863126/assets","upload_url":"https://uploads.github.com/repos/openziti/ziti/releases/296863126/assets{?name,label}","html_url":"https://github.com/openziti/ziti/releases/tag/v1.6.14","id":296863126,"author":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"node_id":"RE_kwDODVFMN84RscWW","tag_name":"v1.6.14","target_commitish":"main","name":"v1.6.14","draft":false,"immutable":false,"prerelease":false,"created_at":"2026-03-13T20:12:09Z","updated_at":"2026-03-13T20:39:52Z","published_at":"2026-03-13T20:24:26Z","assets":[{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/373332423","id":373332423,"node_id":"RA_kwDODVFMN84WQJnH","name":"checksums.sha256.txt","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"text/plain; charset=utf-8","state":"uploaded","size":758,"digest":"sha256:73979f1b1b7d9a58530d4b428b84f193eb821bc2d8d4ff70056d1e968920a711","download_count":14,"created_at":"2026-03-13T20:24:24Z","updated_at":"2026-03-13T20:24:24Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.6.14/checksums.sha256.txt"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/373332420","id":373332420,"node_id":"RA_kwDODVFMN84WQJnE","name":"sbom-v1.6.14.spdx.json","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/json","state":"uploaded","size":1005862,"digest":"sha256:6a1802e5d369683c6cb20aa76859fd75b9cb6a4f4dbee4fa1404821990a6eccf","download_count":4,"created_at":"2026-03-13T20:24:24Z","updated_at":"2026-03-13T20:24:24Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.6.14/sbom-v1.6.14.spdx.json"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/373332421","id":373332421,"node_id":"RA_kwDODVFMN84WQJnF","name":"source-v1.6.14.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":3496559,"digest":"sha256:77a7592c2a7fc16c0ecd9a3c30941481c60e60a3dd374d4db82709c5903e30e9","download_count":5,"created_at":"2026-03-13T20:24:24Z","updated_at":"2026-03-13T20:24:24Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.6.14/source-v1.6.14.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/373332397","id":373332397,"node_id":"RA_kwDODVFMN84WQJmt","name":"ziti-darwin-amd64-1.6.14.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":50930890,"digest":"sha256:292d59748bdb15a9d93f9bf9bfcf29f69d752cd1201b6f415abeb1fde4e0800f","download_count":140,"created_at":"2026-03-13T20:24:20Z","updated_at":"2026-03-13T20:24:24Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.6.14/ziti-darwin-amd64-1.6.14.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/373332395","id":373332395,"node_id":"RA_kwDODVFMN84WQJmr","name":"ziti-darwin-arm64-1.6.14.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":47360378,"digest":"sha256:159d77cbbbd8b3ccc24d95874fcd3aefd9cfcd53601a4f7aa38ef12a24e34bc8","download_count":220,"created_at":"2026-03-13T20:24:20Z","updated_at":"2026-03-13T20:24:24Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.6.14/ziti-darwin-arm64-1.6.14.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/373332394","id":373332394,"node_id":"RA_kwDODVFMN84WQJmq","name":"ziti-linux-amd64-1.6.14.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":52161426,"digest":"sha256:e1e42ef42ccf1d1860e4bf8c29c08c732a27aa2209c16778cc2422e42b0dadc0","download_count":6523,"created_at":"2026-03-13T20:24:20Z","updated_at":"2026-03-13T20:24:24Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.6.14/ziti-linux-amd64-1.6.14.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/373332393","id":373332393,"node_id":"RA_kwDODVFMN84WQJmp","name":"ziti-linux-arm-1.6.14.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":48748621,"digest":"sha256:b93786cf11ea9759eb8cab3d09111de65d2e55e6b9c4ecb56518c0887c6a7b38","download_count":4,"created_at":"2026-03-13T20:24:20Z","updated_at":"2026-03-13T20:24:23Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.6.14/ziti-linux-arm-1.6.14.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/373332419","id":373332419,"node_id":"RA_kwDODVFMN84WQJnD","name":"ziti-linux-arm64-1.6.14.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":48911958,"digest":"sha256:5ed469ddd2a6f7229fa3abcb3d4cfb2adcc81a647df5d6e63d55398696750812","download_count":95,"created_at":"2026-03-13T20:24:24Z","updated_at":"2026-03-13T20:24:26Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.6.14/ziti-linux-arm64-1.6.14.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/373332400","id":373332400,"node_id":"RA_kwDODVFMN84WQJmw","name":"ziti-windows-amd64-1.6.14.zip","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/zip","state":"uploaded","size":42135107,"digest":"sha256:d90c112653bc59f767d9fe1dd5d2db12623d11fe38c3278119100afd223f3a91","download_count":579,"created_at":"2026-03-13T20:24:20Z","updated_at":"2026-03-13T20:24:23Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v1.6.14/ziti-windows-amd64-1.6.14.zip"}],"tarball_url":"https://api.github.com/repos/openziti/ziti/tarball/v1.6.14","zipball_url":"https://api.github.com/repos/openziti/ziti/zipball/v1.6.14","body":"# Release 1.6.14\r\n\r\n## What's New\r\n\r\n* Bug fixes and dependency updates\r\n* Update to Golang v1.26\r\n\r\n## Component Updates and Bug Fixes\r\n\r\n* github.com/openziti/sdk-golang: [v1.2.4 -> v1.2.4-patch1](https://github.com/openziti/sdk-golang/compare/v1.2.4...v1.2.4-patch1)\r\n    * [Issue #892](https://github.com/openziti/sdk-golang/issues/892) - Backport-1.2.4: Handle differences in xgress eof/end-of-circuit handling by adding a capabilities exchange\r\n\r\n* github.com/openziti/ziti: [v1.6.13 -> v1.6.14](https://github.com/openziti/ziti/compare/v1.6.13...v1.6.14)\r\n    * [Issue #3646](https://github.com/openziti/ziti/issues/3646) - Backport-1.6: Use xgress_common.Connection type for xgress_transport and xgress_proxy\r\n\r\n"},{"url":"https://api.github.com/repos/openziti/ziti/releases/295945261","assets_url":"https://api.github.com/repos/openziti/ziti/releases/295945261/assets","upload_url":"https://uploads.github.com/repos/openziti/ziti/releases/295945261/assets{?name,label}","html_url":"https://github.com/openziti/ziti/releases/tag/v2.0.0-pre5","id":295945261,"author":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"node_id":"RE_kwDODVFMN84Ro8Qt","tag_name":"v2.0.0-pre5","target_commitish":"main","name":"v2.0.0-pre5","draft":false,"immutable":false,"prerelease":true,"created_at":"2026-03-12T01:59:57Z","updated_at":"2026-03-12T02:07:16Z","published_at":"2026-03-12T02:07:16Z","assets":[{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/371981961","id":371981961,"node_id":"RA_kwDODVFMN84WK_6J","name":"checksums.sha256.txt","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"text/plain; charset=utf-8","state":"uploaded","size":790,"digest":"sha256:844a5fb9ab97ed2e310adea519b3a3b024d316222cc881805f3fffbc1a81c3be","download_count":4,"created_at":"2026-03-12T02:07:13Z","updated_at":"2026-03-12T02:07:13Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre5/checksums.sha256.txt"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/371981959","id":371981959,"node_id":"RA_kwDODVFMN84WK_6H","name":"sbom-v2.0.0-pre5.spdx.json","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/json","state":"uploaded","size":999884,"digest":"sha256:eff75f94f78b22882ad3c7cf2f02266557764cdaa705ceba78354f1637b9525c","download_count":1,"created_at":"2026-03-12T02:07:13Z","updated_at":"2026-03-12T02:07:13Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre5/sbom-v2.0.0-pre5.spdx.json"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/371981960","id":371981960,"node_id":"RA_kwDODVFMN84WK_6I","name":"source-v2.0.0-pre5.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":3765221,"digest":"sha256:4b0e1c2c8aae2e1fe8c4abe7dbb0a944427916d859698424c70b5a9bee06ca46","download_count":2,"created_at":"2026-03-12T02:07:13Z","updated_at":"2026-03-12T02:07:14Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre5/source-v2.0.0-pre5.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/371981940","id":371981940,"node_id":"RA_kwDODVFMN84WK_50","name":"ziti-darwin-amd64-2.0.0-pre5.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":53920402,"digest":"sha256:2a7929c252fa25eab47468bd0c92323cba407a7dad3ef4d94d221729876058a3","download_count":5,"created_at":"2026-03-12T02:07:11Z","updated_at":"2026-03-12T02:07:13Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre5/ziti-darwin-amd64-2.0.0-pre5.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/371981958","id":371981958,"node_id":"RA_kwDODVFMN84WK_6G","name":"ziti-darwin-arm64-2.0.0-pre5.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":50287957,"digest":"sha256:1650ab9c8ecebc6198e03932ba6187e84eb3ea189ea262715e7780b80e34aa9a","download_count":4,"created_at":"2026-03-12T02:07:13Z","updated_at":"2026-03-12T02:07:15Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre5/ziti-darwin-arm64-2.0.0-pre5.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/371981937","id":371981937,"node_id":"RA_kwDODVFMN84WK_5x","name":"ziti-linux-amd64-2.0.0-pre5.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":55227945,"digest":"sha256:8b7ec29a1dcafacfd707914a057fefbcacd06df02fac7d3900d17f166b9dc30e","download_count":191,"created_at":"2026-03-12T02:07:11Z","updated_at":"2026-03-12T02:07:13Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre5/ziti-linux-amd64-2.0.0-pre5.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/371981941","id":371981941,"node_id":"RA_kwDODVFMN84WK_51","name":"ziti-linux-arm-2.0.0-pre5.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":51735598,"digest":"sha256:fbbf6f0adc526be0e861e51515c25c4742542d26d4aca09ec83c1f4c3ef34724","download_count":5,"created_at":"2026-03-12T02:07:11Z","updated_at":"2026-03-12T02:07:13Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre5/ziti-linux-arm-2.0.0-pre5.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/371981939","id":371981939,"node_id":"RA_kwDODVFMN84WK_5z","name":"ziti-linux-arm64-2.0.0-pre5.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":51741015,"digest":"sha256:5b5ca93988183820a2c08da14f937e2b3aa38cc2cacf2b43056f9d5b283b78e0","download_count":7,"created_at":"2026-03-12T02:07:11Z","updated_at":"2026-03-12T02:07:13Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre5/ziti-linux-arm64-2.0.0-pre5.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/371981938","id":371981938,"node_id":"RA_kwDODVFMN84WK_5y","name":"ziti-windows-amd64-2.0.0-pre5.zip","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/zip","state":"uploaded","size":44621390,"digest":"sha256:6efbe5b7f627c6a4ff5077f2cbc6be717aa0773c4e16ba7543077c9c3cac74c5","download_count":5,"created_at":"2026-03-12T02:07:11Z","updated_at":"2026-03-12T02:07:13Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre5/ziti-windows-amd64-2.0.0-pre5.zip"}],"tarball_url":"https://api.github.com/repos/openziti/ziti/tarball/v2.0.0-pre5","zipball_url":"https://api.github.com/repos/openziti/ziti/zipball/v2.0.0-pre5","body":"# Release 2.0.0\n\n## What's New\n\nThis is the next major version release of OpenZiti, following the 1.0 release in April 2024. \nOf particular note is that HA controllers are now considered ready for general use. \nThis release also introduces a new permissions model, OIDC/JWT token-based enrollment, \nclustering performance improvements, and a number of other features and fixes. Because \nsome of these changes are not backwards compatible with older routers, we're marking this \nas a major version bump.\n\n### HA Controllers are now considered ready for general use\n\nThis is a pretty big milestone and marks the completion of work that's been ongoing for a couple of years.\nThe HA work has brought with it some notable changes. Authentication now uses JWTs by default. Using JWTs\nmeans the controllers don't need to store session and propagate them to the routers. This removes a bottleneck\nfrom the network and allows the load to be more easily distributed among controllers and routers.\n\nTo support distributed authentication, the routers now get a bespoke version of the data model. In addition\nto enabling distributed authentication this will allow us to remove the need for service polling and further \nreduce the load on controllers in the future.\n\n### Router Compatibility\n\nRelated to the JWT work, routers with version 2.+ will only work with controllers that are version 2.+. This means\nto upgrade your network, controllers should be upgraded first. Routers can then be upgraded individually.\n\n2.x routers should still work fine with older router versions.\n\nWe try very hard to avoid breaking changes like this, but sometimes the engineering trade-offs lead there. This change\nwas first made in the 1.7 release. That release has not been marked stable, and we have no plans to do so, because\nof the backwards incompatibility. \n\nIf you need to support in the 1.6.12+ range, see the section \"OIDC enabled by default\".\n\n### New Permissions Model (BETA)\n\nAs one feature goes out of beta, another arrives into beta. This release introduces a new permissions system\nfor more fine grained control to the management API. It's not expected to change, but may do so based on feedback\nfrom users.\n\n### Updated Release Process\n\nWe have moved to creating `-preN` releases for major and minor versions. This way we can put out release candidates,\nor feature previews and put them through internal testing and let interested folks from the community try them out.\nThen, when we're ready, we can run the full validation suite against the last pre-release and retag it. \n\nPatch releases won't have `-preN` and should contain only high priority bug fixes.\n\n### Deprecation Cleanup\n\nSince we already have a breaking change, we're removing some other backwards compatibility code.\n\n* Controller managed links \n    * Router managed links were introduced in v0.30.0. \n    * If you're upgrading from an older versions, you'll want to upgrade to the latest 1.x release before jumping to 2.x\n    * Github tracking issue: https://github.com/openziti/ziti/issues/3512\n* `ziti edge create identity <type>`\n    * Identity types other than router were removed in v0.30.2\n    * The `type` can be dropped from the CLI command\n    * Github tracking issue: https://github.com/openziti/ziti/issues/3532\n* Terminator create/update/delete events\n    * These have been superseded by entity change events, which also have create/update/delete events for terminators\n    * Entity change events were introduced in v0.28.0\n    * Github tracking issue: https://github.com/openziti/ziti/issues/3531\n* `xgress_edge_tunnel` v1\n    * This is the first implementation of the tunneler in edge-router code (ER/T) which used legacy api sessions and services\n    * The v2 version uses the router data model and was introduced in v0.30.x\n    * Github tracking issue: https://github.com/openziti/ziti/issues/3516\n\n### Legacy Session Deprecation\n\nOIDC sessions are now preferred. They are the default, or will become the default for SDKs and tunnelers. They are also required\nwhen running HA. Legacy API and service session are now deprecated and will be removed in the OpenZiti v3.0.0 release. \n\n### Additional Features\n\n* Controllers can now optionally bind APIs using an OpenZiti identity\n* `ziti edge login` now supports the `--network-identity` flag to authenticate and establish connections through the Ziti overlay network\n* `ziti edge login` now supports using a bearer token with `--token` for authentication. The token is expected to be\n  provided as just the JWT, not with the \"Bearer \" prefix\n* Identity configuration can now be loaded from files or environment variables for flexible deployment scenarios\n* Identities can now be provisioned just-in-time through OIDC/JWT token-based enrollment\n* Multiple model updates can now be in-flight at the same time, improving clustering performance\n* Authentication-related model updates can now be non-blocking and even dropped if the system is too busy\n* Routers now provide more error context to SDKs for terminator errors, enabling better retry behavior\n* New `proxy.v1` config type for dynamic service proxies (originally released in 1.7.0)\n* New alert event type for surfacing operational issues to network operators - Beta (originally released in 1.7.0)\n* New Azure Service Bus event sink for streaming controller events, contributed by @ffaraone (originally released in 1.7.0)\n* Bundled ZAC upgraded to 4.0\n* Build updated to Go 1.25\n* CLI cleaned up to remove calls to `os.Exit`, making it more friendly for embedding\n* OIDC is now enabled by default\n* Controller Edge APIs now return `WWW-Authenticate` response headers on `401 Unauthorized` responses, giving clients actionable information about which auth methods are accepted and what went wrong\n* HA Controllers can be marked as 'preferredLeader' via config\n* Dynamic cost range for smart routing expanded beyond the previous 64K limit\n* Dial failures now return the circuit ID and error information for easier debugging\n* Router-to-controller control channels now support multiple underlays with priority-based message routing\n* The dialing identity's ID and name are now forwarded to the hosting SDK\n* Controllers can now dial routers to establish control channels, enabling connectivity when routers are behind firewalls (Beta)\n\n## Basic Permission System (BETA)\n\nAdded a basic permission system that allows more control over identity access to controller management API operations. \nThis replaces the previous binary admin/non-admin model with a more flexible permission system.\n\n**NOTE:** This feature is in BETA, primarily so we can get feedback on which permissions make sense. The implementation is unlikely to change\nbut the set of exposed permissions may grow, shrink or change based on user feedback. \n\n### Permission Model\n\nThe permission system supports three levels of authorization:\n\n  1. **Global Permissions**: System-wide access levels\n     - `admin` - Full access to all operations. This is still controlled by the `isAdmin` flag on identity\n     - `admin_readonly` - Read-only access to all resources except debugging facilities inspect and validate\n\n  2. **Entity-Level Permissions**: Full CRUD access to specific entity types\n     - Granting an entity-level permission (e.g., `service`) provides complete create, read, update, and delete access for that entity type\n\n  3. **Action-Level Permissions**: Specific operation access on entity types\n     - Fine-grained control using the pattern `<entity>.<action>` (e.g., `service.read`, `identity.update`)\n     - Supports `create`, `read`, `update`, and `delete` actions per entity type\n\n### Supported Entity Permissions\n\nThe following entity-level permissions are available:\n\n- `auth-policy` - Authentication policy management\n- `ca` - Certificate Authority management\n- `config` - Configuration management\n- `config-type` - Configuration type management\n- `edge-router-policy` - Edge router policy management\n- `enrollment` - Enrollment management\n- `external-jwt-signer` - External JWT signer management\n- `identity` - Identity management\n- `posture-check` - Posture check management\n- `router` - Edge and transit router management\n- `service` - Service management\n- `service-policy` - Service policy management\n- `service-edge-router-policy` - Service edge router policy management\n- `terminator` - Terminator management\n- `ops` - Operational resources (API sessions, sessions, circuits, links, inspect and validate)\n\n### Permission Assignment\n\nPermissions are assigned to identities via the `permissions` field in the identity resource. Multiple permissions can be granted to a single identity, and permissions are additive.\n\n### Cross-Entity Operations\n\nListing related entities through an entity's endpoints requires appropriate permissions for the related entity type. For example:\n- Listing services for a service-policy requires `service.read` permission\n- Listing identities for an edge-router-policy requires `identity.read` permission\n- Listing configs for a service requires `config.read` permission\n\n**NOTE:** \nMore permissions than expected may be required when performing actions through the CLI or ZAC. Take for example, when an identity \nhas `config.create` and is attempting to create a new config. The CLI may fail if the identity doesn't have `config-type.read`\nas well because it will need to look up the config type id that corresponds to the given config type name.\n\nSimilar cross entity read permissions may be required when creating services.\n\n### Admin Protection\n\nNon-admin identities cannot:\n- Create identities with the `isAdmin` flag\n- Create identities with any permissions granted\n- Modify admin-related fields on existing identities\n- Update or delete admin identities\n- Grant permissions to identities\n\nThese protections ensure that privilege escalation is prevented and admin access remains controlled.\n\n\n## Binding Controller APIs With Identity\n\nController APIs can now be bound to an OpenZiti overlay network identity, allowing secure communication through\nthe Ziti network. This is useful for scenarios where you want to expose controller APIs only through the overlay\nnetwork rather than on a standard network interface.\n\n### Configuration Structure\n\nA standard `bindPoint` configuration looks like this:\n```text\n    bindPoints:\n      - interface: 127.0.0.1:18441\n        address: 127.0.0.1:18441\n```\n\nTo bind controller APIs to an OpenZiti identity, add an additional `identity` block to your `bindPoints`. The\nidentity configuration specifies where to load the Ziti identity file and which service to bind it to:\n\n```text\n    bindPoints:\n      - interface: 127.0.0.1:18441\n        address: 127.0.0.1:18441\n      - identity:\n          file: \"c:/temp/ctrl.testing/ctrl.identity.json\"\n          service: \"mgmt\"\n```\n\n### Supported Configuration Options\n\n- `file`: Path to a Ziti identity JSON file containing the controller's identity and enrollment certificate\n- `env`: Name of an environment variable containing a base64-encoded Ziti identity (alternative to `file`)\n- `service`: The name of the Ziti service to bind the controller API to\n\n### Using Environment Variables\n\nFor deployments where storing identity files on disk is not preferred, you can reference a base64-encoded\nidentity file from an environment variable. The environment variable should contain the base64-encoded contents\nof the identity JSON file.\n\nFor example, if an environment variable named `ZITI_CTRL_IDENTITY` contains a base64-encoded identity file:\n\n```text\n    bindPoints:\n      - interface: 127.0.0.1:18441\n        address: 127.0.0.1:18441\n      - identity:\n          env: ZITI_CTRL_IDENTITY\n          service: \"mgmt\"\n```\n\n### IPv6 Support\n\nBoth IPv4 and IPv6 addresses are supported for standard bind points. IPv6 addresses should be specified in bracket\nnotation with a port number:\n\n```text\n    bindPoints:\n      - interface: \"[::1]:18441\"\n        address: \"[::1]:18441\"\n      - identity:\n          file: \"/path/to/identity.json\"\n          service: \"mgmt\"\n```\n\n## CLI Enhancements for Identity-Based Connections\n\nThe `ziti edge login` command and REST client utilities have been enhanced to support identity-based connections\nthrough the Ziti overlay network.\n\n### New `--network-identity` Flag for `ziti edge login`\n\nThe `ziti edge login` command now includes a `--network-identity` flag that allows you to authenticate to a Ziti\ncontroller through the overlay network using a Ziti identity:\n\n```bash\nziti edge login https://ziti.mgmt.apis.local:1280 \\\n  --username myuser \\\n  --password mypass \\\n  --network-identity /path/to/identity.json\n```\n\nThis is useful when the controller is only accessible through the Ziti overlay network or when you want to ensure\nall communication to the controller flows through the overlay for security purposes.\n\n### Identity Resolution Order\n\nWhen establishing connections, identities are resolved in the following order:\n\n1. **Command-line flag**: The `--network-identity` flag takes precedence\n2. **Environment variable**: If `ZITI_CLI_NETWORK_ID` is set and contains a base64-encoded identity, it is used\n3. **Cached identity file**: If a network identity was saved from a previous login in the Ziti config directory, it may be used\n\nThis layered approach allows for flexibility in deployment scenarios:\n- Development: Use command-line flags for quick testing\n- Automation: Use environment variables in CI/CD pipelines\n- Production: Cache identities securely for repeated access\n\n#### Dialing Modes When Authenticating\n\nThe CLI supports two dialing modes:\n\n**Intercept-based Dialing (Default)**\nBy default, URLs are expected to leverage intercepts. Create a service with an appropriate intercept config and use\nthe intercept address when dialing. This is the standard mode for most use cases. For example, given a service with\nthe intercept `ziti.mgmt.apis.local`\n```bash\nziti edge login https://ziti.mgmt.apis.local:1280 \\\n  --username myuser \\\n  --password mypass \\\n  --network-identity /path/to/identity.json\n```\n\n**Identity-aware Dialing (Addressable Terminators)**\nTo support addressable terminators-based dialing, specify a user in the URL. This activates dial-by-identity\nfunctionality. The URL format should be `identity-to-dial@service-name-to-dial`. For example:\n```bash\nziti edge login https://my-identity@my-service:1280 \\\n  --username myuser \\\n  --password mypass \\\n  --network-identity /path/to/identity.json\n```\n\nIn this mode, the transport extracts the identity from the URL and uses it to establish a direct connection to\nthe specified service via the addressable terminator.\n\n\n## OIDC/JWT Token-based Enrollment\n\nOpenZiti now supports provisioning identities just-in-time through OIDC/JWT token enrollment. External identity \nproviders can be configured to allow identities to enroll using JWT tokens, with support for the resulting \nidentities to use certificate or token authentication.\n\n### External JWT Signer Configuration\n\nExternal JWT signers are configured via the Edge Management API to define enrollment behavior with the following new \nenrollment-specific properties:\n\n- **enrollToCertEnabled** - When enabled, identities can exchange a JWT token and a certificate signing request (CSR) \n        for a client certificate during enrollment. The certificate can then be used for standard certificate-based\n        authentication.\n\n- **enrollToTokenEnabled** - When enabled, identities can use a JWT token to enroll. The current token or future tokens\n        may be used for authentication.\n\n- **enrollNameClaimsSelector** - Specifies which JWT claim contains the identity name. Accepts a JSON pointer \n        (e.g., `/preferred_username`) or a simple property name (e.g., `preferred_username`, automatically converted to\n        `/preferred_username`). Defaults to `/sub` if not specified. The extracted value becomes the identity name in Ziti.\n\n- **enrollAttributeClaimsSelector** - Specifies which JWT claims to extract as identity attributes during enrollment.\n        Accepts a JSON pointer (e.g., `/roles`) or a simple property name (e.g., `roles`). Extracted attributes are \n        applied to the newly enrolled identity for use in authorization policies.\n\n- **enrollAuthPolicyId** - Specifies the authentication policy to apply to newly enrolled identities. This determines\n        what authentication methods are available for the identity post-enrollment.\n\nAdditionally the existing property named **claimsProperty** that specifies external id to match identities to:\n\n- now supports a JSON pointer (e.g., `/id`) or a simple property name (e.g., `id`)\n- is used to populate the `externalId` field of the identity\n\n### Enrollment Paths\n\n#### Certificate Enrollment (enrollToCertEnabled)\n\nWhen certificate enrollment is enabled, unauthenticated users can:\n\n1. Obtain a list of available IdPs from the public Edge Client API `GET /external-jwt-signers` endpoint, where \n   `enrollToCertEnabled` is set to `true`\n2. Obtain a JWT from the configured OIDC provider\n3. Generate a certificate signing request (CSR)\n4. Submit an enrollment request with the JWT and CSR\n5. Have their identity created in Ziti with attributes extracted from JWT claims\n6. Receive a signed client certificate for certificate-based authentication\n\n#### Token Enrollment (enrollToTokenEnabled)\n\nWhen token enrollment is enabled, unauthenticated users can:\n\n1. Obtain a list of available IdPs from the public Edge Client API `GET /external-jwt-signers` endpoint, where \n   `enrollToTokenEnabled` is set to `true`\n1. Obtain a JWT from the configured OIDC provider\n2. Submit an enrollment request with the JWT\n3. Have their identity created in Ziti with attributes extracted from JWT claims\n4. Receive a Ziti API token for token-based authentication\n\n### Edge Management API\n\nThe Edge Management API provides full CRUD operations for configuring external JWT signers:\n\n- `POST /external-jwt-signers` - Create a new external JWT signer with all configuration options\n- `GET /external-jwt-signers` - List all configured external JWT signers\n- `GET /external-jwt-signers/{id}` - Retrieve a specific signer configuration\n- `PUT /external-jwt-signers/{id}` - Update all fields of a signer\n- `PATCH /external-jwt-signers/{id}` - Partially update a signer\n- `DELETE /external-jwt-signers/{id}` - Delete a signer\n\n### Edge Client API\n\nThe Edge Client API exposes a reduced set of external JWT signer information for unauthenticated enrollment requests:\n\n- `GET /external-jwt-signers` - List available JWT signers with enrollment capabilities\n\nThe client API response includes the following fields for each signer:\n\n- `name` - Signer name\n- `externalAuthUrl` - URL where users obtain JWT tokens\n- `clientId` - OIDC client ID\n- `scopes` - Requested OIDC scopes\n- `openIdConfigurationUrl` - OIDC discovery endpoint\n- `audience` - Expected token audience\n- `targetToken` - Token type to use (ACCESS or ID)\n- **`enrollToCertEnabled`** - Flag indicating certificate enrollment is available\n- **`enrollToTokenEnabled`** - Flag indicating token enrollment is available\n\n### CLI Commands\n\n**Create an external JWT signer with enrollment options:**\n```\nziti edge controller create ext-jwt-signer <name> <issuer> \\\n  --jwks-endpoint <url> \\\n  --audience <audience> \\\n  --enroll-to-cert \\\n  --enroll-to-token=false \\\n  --enroll-name-claims-selector preferred_username \\\n  --enroll-attr-claims-selector roles \\\n  --enroll-auth-policy <policy-id-or-name>\n```\n\n**Update enrollment options on an existing signer:**\n```\nziti edge controller update ext-jwt-signer <name|id> \\\n  --enroll-to-cert \\\n  --enroll-auth-policy <policy-id-or-name>\n```\n\n**List external JWT signers:**\n```\nziti edge controller list ext-jwt-signers\n```\n\n## Clustering Performance Improvements\n\nIn previous releases, model updates were submitted to raft one at at time. This prevented \nraft from being efficient by allowing command batching. This release allows multiple \nmodel updates to be in-flight at the same time. \n\nNew Configuration Options\n\n1. Raft Apply Timeout (cluster.applyTimeout)\n\nLocation: Controller configuration file, under the cluster section\nType: Duration\nDefault: 5s\nDescription: Timeout for applying commands to the Raft distributed log. Commands that exceed this timeout will trigger adaptive rate limiter backoff.\n\nExample:\n```\ncluster:\n  applyTimeout: 10s\n```\n\n2. Cluster Rate Limiter Configuration (cluster.rateLimiter)\n\nA new adaptive rate limiter that controls the submission of commands to the Raft cluster. Unlike the existing command rate limiter, this specifically manages in-flight Raft operations with adaptive window sizing.\n\nConfiguration Structure:\n```\ncluster:\n  rateLimiter:\n    enabled: true\n    minSize: 5\n    maxSize: 250\n    timeout: 30s\n```\n\nSub-options:\n\n  - enabled (boolean)\n    - Default: true\n    - Description: Enable/disable adaptive rate limiting for Raft command submission\n  - minSize (integer)\n    - Default: 5\n    - Minimum: 1\n    - Description: Minimum window size for concurrent in-flight Raft operations\n  - maxSize (integer)\n    - Default: 250\n    - Description: Maximum window size for concurrent in-flight Raft operations. Must be >= minSize\n  - timeout (duration)\n    - Default: 30s\n    - Description: Time after which outstanding work is assumed to have failed if not marked completed\n\n3. Restart Self on Snapshot (cluster.restartSelfOnSnapshot)\n\nLocation: Controller configuration file, under cluster section\nType: Boolean\nDefault: false\nDescription: When true, the controller will automatically restart itself when restoring a snapshot to an initialized system. When false, the controller will exit with code 0, requiring external process management to restart it.\n\nExample:\n```\ncluster:\n    restartSelfOnSnapshot: true\n```\n\n### New Metrics\n\nThe adaptive rate limiter exposes three new metrics:\n\n  1. raft.rate_limiter.queue_size (gauge)\n    - Current number of operations queued/in-flight\n  2. raft.rate_limiter.work_timer (timer)\n    - Duration of rate-limited operations\n  3. raft.rate_limiter.window_size (gauge)\n    - Current adaptive window size\n\n## Rate Limiter Algorithm Improvements\n\nThe adaptive rate limiter tracker now uses a success rate metric to decide when to grow or shrink its\nconcurrency window, instead of using raw queue position. An exponentially decaying histogram tracks the\nratio of successes to backoffs. When the success rate exceeds a configurable threshold, the window is\ngrown by a multiplicative increase factor. When it falls below the threshold, the window is shrunk by a\nmultiplicative decrease factor. The check intervals for increase and decrease are independently configurable.\n\nThis approach produces smoother, more stable window adjustments under varying load, avoiding the\nover-aggressive shrinking that could occur when queue position alone was used as the signal.\n\nThis specific rate limiter implementation is used in three places:\n* **Controller TLS handshake rate limiting** - controls the rate of incoming TLS handshakes on the controller\n* **Raft command submission** - controls the rate of commands submitted to the Raft distributed log\n* **Router control channel rate limiting** - controls the rate of requests from the router to the controller\n\nNote: this work was done in advance of enabling the TLS handshake rate limiter by default. The TLS\nhandshake rate limiter is not yet enabled by default, but can be enabled via the `tls.rateLimiter`\nconfiguration section.\n\nNew configuration options (available under each `rateLimiter` section):\n\n  - successThreshold (float)\n    - Default: 0.9\n    - Description: Success rate threshold above which the window size will be increased and below which it will be decreased\n  - increaseFactor (float)\n    - Default: 1.02\n    - Description: Multiplier applied to the current window size when growing. Must be greater than 1\n  - decreaseFactor (float)\n    - Default: 0.9\n    - Description: Multiplier applied to the current window size when shrinking. Must be between 0 and 1\n  - increaseCheckInterval (integer)\n    - Default: 10\n    - Description: Number of successes between window size increase checks\n  - decreaseCheckInterval (integer)\n    - Default: 10\n    - Description: Number of backoffs between window size decrease checks\n\n## Background Processing for Identity Updates\n\nIdentity environment and authenticator updates that occur during authentication are now processed asynchronously in the background. \nThis prevents authentication requests from blocking when the system is under load, significantly improving resilience during thundering herd scenarios.\n\nWhen the background queue fills up, updates can be dropped as they will be refreshed on the next authentication attempt. \nThis allows the system to gracefully handle load spikes without impacting authentication performance.\n\nFor now, dropping entries when the queue fills will be disabled by default, but can be enabled, see below.\n\n### Configuration\n\nA new `command.background` configuration section controls the background processing behavior:\n\n```yaml\n  command:\n    background:\n      enabled: true           # Enable background processing (default: true)\n      queueSize: 1000        # Maximum queue size (default: 1000)\n      dropWhenFull: true     # Drop updates when queue is full (default: false)\n      delayThreshold: 50ms   # The threshold for how long updates are taking before starting to background updates\n```\n\nNote that the `commandRateLimiter` configuration section may instead be specified under `command` as `rateLimiter`.\n\nExample:\n\n```yaml\n  command:\n    background:\n      enabled: true\n      queueSize: 250\n      dropWhenFull: false\n      delayThreshold: 50ms\n    rateLimiter:\n      enabled:   true\n      maxQueued: 25\n```\n\nNote that if command rate limiter configuration is specified in both locations, the settings under `command` will take \nprecedence. The standalone `commandRateLimiter` section may be deprecated in the future.\n\n### Metrics\n\nWhen background processing is enabled, the following metrics are exposed:\n\n- command.background.queue_size - Current number of queued background tasks\n- command.background.worker_count - Current number of worker goroutines\n- command.background.busy_workers - Number of workers currently processing tasks\n- command.background.work_timer - Timer tracking background task execution (includes histogram, meter, and count)\n- command.background.dropped_entries - Count of dropped updates when queue is full (only when dropWhenFull is enabled)\n\n## New proxy.v1 Config Type\n\n*Originally released in 1.7.0*\n\nAdded support for dynamic service proxies with configurable binding and protocol options.\nThis allows Edge Routers and Tunnelers to create proxy endpoints that can forward traffic for Ziti services.\n\nThis differs from intercept.v1 in that intercept.v1 will intercept traffic on specified\nIP addresses or DNS entries to forward to a service using tproxy or tun interface,\ndepending on implementation.\n\nA proxy on the other hand will just start a regular TCP/UDP listener on the configured port,\nso traffic will have to be configured for that destination.\n\nExample proxy.v1 Configuration:\n\n```\n  {\n    \"port\": 8080,\n    \"protocols\": [\"tcp\"],\n    \"binding\": \"0.0.0.0\"\n  }\n```\n\nConfiguration Properties:\n  - port (required): Port number to listen on (1-65535)\n  - protocols (required): Array of supported protocols (tcp, udp)\n  - binding (optional): Interface to bind to. For the ER/T defaults to the configured lanIF config property.\n\nThis config type is currently supported by the ER/T when running in either proxy or tproxy mode.\n\n## Alert Events (BETA)\n\n*Originally released in 1.7.0*\n\nA new alert event type has been added to allow Ziti components to emit alerts for issues that network operators can address.\nAlert events are generated when components encounter problems such as service configuration errors or resource\navailability issues.\n\nAlert events include:\n  - Alert source type and ID (currently supports routers, with controller and SDK support planned for future releases)\n  - Severity level (currently supports error, with info and warning planned for future releases)\n  - Alert message and supporting details\n  - Related entities (router, identity, service, etc.) associated with the alert\n\nExample alert event when a router cannot bind a configured network interface:\n\n```\n  {\n    \"namespace\": \"alert\",\n    \"event_src_id\": \"ctrl1\",\n    \"timestamp\": \"2021-11-08T14:45:45.785561479-05:00\",\n    \"alert_source_type\": \"router\",\n    \"alert_source_id\": \"DJFljCCoLs\",\n    \"severity\": \"error\",\n    \"message\": \"error starting proxy listener for service 'test'\",\n    \"details\": [\n      \"unable to bind eth0, no address\"\n    ],\n    \"related_entities\": {\n      \"router\": \"DJFljCCoLs\",\n      \"identity\": \"DJFljCCoLs\",\n      \"service\": \"3DPjxybDvXlo878CB0X2Zs\"\n    }\n  }\n```\n\nAlert events can be consumed through the standard event system and logged to configured event handlers for monitoring and alerting purposes.\n\nThese events are currently in Beta, as the format is still subject to change. Once they've been in use in production for a while\nand proven useful, they will be marked as stable.\n\n## Azure Service Bus Event Sink\n\n*Originally released in 1.7.0. Contributed by @ffaraone.*\n\nAdds support for streaming controller events to Azure Service Bus.\nThe new logger enables real-time event streaming from the OpenZiti controller to Azure Service Bus\nqueues or topics, providing integration with Azure-based monitoring and analytics systems.\n\nTo enable the Azure Service Bus event logger, add configuration to the controller config file under the events section:\n\n```\n  events:\n    serviceBusLogger:\n      subscriptions:\n        - type: circuit\n        - type: session\n        - type: metrics\n          sourceFilter: .*\n          metricFilter: .*\n        # Add other event types as needed\n      handler:\n        type: servicebus\n        format: json\n        connectionString: \"Endpoint=sb://your-namespace.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=your-key\"\n        topic: \"ziti-events\"          # Use 'topic' for Service Bus topic\n        # queue: \"ziti-events-queue\"  # Or use 'queue' for Service Bus queue\n        bufferSize: 100                # Optional, defaults to 50\n```\n\n- Required configuration:\n    - format: Event format, currently supports only json\n    - connectionString: Azure Service Bus connection string\n    - Either topic or queue: Destination name (mutually exclusive)\n\n- Optional configuration:\n    - bufferSize: Internal message buffer size (default: 50)\n\n## OIDC is now enabled by default\n\nThe controller now automatically adds the `edge-oidc` API binding to any web listener that hosts\nthe `edge-client` API, even when `edge-oidc` is not explicitly listed in the `web` section of the\nconfiguration. This means OIDC-based authentication is available out of the box without requiring\nchanges to existing controller configurations.\n\n### Where OIDC binds\n\nThe `edge-oidc` binding is added to the same web listener(s) as `edge-client`. If `edge-client` is\nhosted on `127.0.0.1:1280`, the OIDC endpoints will be available on that same address under the\n`/oidc` path (e.g. `https://127.0.0.1:1280/oidc/.well-known/openid-configuration`).\n\nThe controller capabilities returned by `GET /version` will include `OIDC_AUTH` and the\n`edge-oidc` entry will be present in `apiVersions` when OIDC is active.\n\n### Opting out\n\nIf OIDC should not be enabled automatically, set `disableOidcAutoBinding: true` under `edge.api`:\n\n```yaml\nedge:\n  api:\n    address: 127.0.0.1:1280\n    sessionTimeout: 30m\n    disableOidcAutoBinding: true\n```\n\nWhen `disableOidcAutoBinding` is `true`, OIDC will only be active if `edge-oidc` is explicitly\nlisted as a binding in the `web` section. \n\nWhen OIDC is not enabled, all (SDK) clients will revert to using legacy authentication.\nLegacy authentication does not support multiple controllers or HA in general. Clients will treat\ntheir controller as if it is a single controller instance and will function as if no other controllers\nexist.\n\n\n## WWW-Authenticate Headers\n\nThe controller's Edge APIs now include `WWW-Authenticate` headers on `401 Unauthorized` responses,\nper issuer: https://github.com/openziti/ziti/issues/3356. These headers provide insight into why\na token was rejected. The main benefit of these headers is to convey information for JWT backed\nAPI Sessions and API Session authentication where a token may not have been provided (missing),\nis used beyond its expiration date (expired), or the token has become invalid for any other\nreason (invalid).\n\n`www-authenticate` headers are provided as a single header instance with multiple challenge values\nseparate by commas.\n\n### No Credentials Provided\n\nWhen a request hits a protected endpoint without any credentials, a single `WWW-Authenticate` header\nis returned listing both accepted auth schemes as comma-separated challenges:\n\n```\nWWW-Authenticate: zt-session realm=\"zt-session\" error=\"missing\" error_description=\"no matching token was provided\",Bearer realm=\"openziti-oidc\" error=\"missing\" error_description=\"no matching token was provided\"\n```\n\n### Token Errors\n\nWhen a token is present but cannot be accepted, the header identifies the scheme and what went wrong:\n\n```\nWWW-Authenticate: Bearer realm=\"openziti-oidc\" error=\"expired\" error_description=\"token expired\"\nWWW-Authenticate: Bearer realm=\"openziti-oidc\" error=\"invalid\" error_description=\"token is invalid\"\nWWW-Authenticate: zt-session realm=\"zt-session\" error=\"invalid\" error_description=\"token is invalid\"\n```\n\n### OIDC External JWT — Primary Authentication\n\nWhen an auth policy requires an external JWT signer for primary authentication (e.g., a PKCE flow\nbacked by an ext-jwt signer), the header identifies which signers are accepted and what went wrong.\nMultiple accepted signers are pipe-delimited in the `id` and `issuer` parameters:\n\n```\nWWW-Authenticate: Bearer realm=\"openziti-primary-ext-jwt\" error=\"missing\" error_description=\"no matching token was provided\" id=\"signer-id-1|signer-id-2\" issuer=\"https://issuer1.example.com|https://issuer2.example.com\"\n```\n\nThe `error` value follows the same `missing`/`expired`/`invalid` pattern as standard bearer token errors.\n\n### OIDC External JWT — Secondary / MFA Authentication\n\nWhen an auth policy requires an external JWT signer as a secondary factor (step-up after primary\nauth succeeds), the header identifies the single required signer. The `error` value follows the\nsame `missing`/`expired`/`invalid` pattern:\n\n```\nWWW-Authenticate: Bearer realm=\"openziti-secondary-ext-jwt\" error=\"missing\" error_description=\"no matching token was provided\" id=\"<signer-id>\" issuer=\"<issuer>\"\n```\n\n### Anonymous Endpoints\n\nUnauthenticated endpoints such as version information do not return `WWW-Authenticate` headers.\n\n## HA Preferred Leaders\n\nControllers can be marked as a preferred leader. \n\n**Example Config**\n```yaml\ncluster:\n  dataDir: /home/{{ .Model.MustVariable \"credentials.ssh.username\" }}/fablab/ctrldata\n  preferredLeader: true\n```\n\nIf a controller that is not marked preferredLeader becomes a preferredLeader, it will check \nif there's a node available that is marked as preferred. If there is one, or one later\njoins the cluster, the non-preferred node will attempt to transfer leadership to the \nnode that is marked as preferred.\n\n## Expanded Dynamic Cost Range\n\nThe dynamic cost range for smart routing has been expanded beyond the previous 64K limit. Under high\nload, terminators could saturate the cost space, making dynamic cost values meaningless for routing\ndecisions and leading to uneven load distribution. The expanded range allows for more granular cost\ndifferentiation even under heavy load.\n\n## Circuit ID and Error in Dial Failures\n\nDial failures now return the circuit ID and, when available, the error that caused the circuit to fail.\nPreviously, the circuit ID was only returned on successful dials. Note that SDKs will need to be\nupdated to surface the circuit id when a dial failure happens.\n\n## Multi-Underlay Control Channels\n\nRouter-to-controller control channels now support multiple underlays with priority-based message routing.\nThis allows time-sensitive control messages (heartbeats, routing, circuit requests) to be separated from\noperational data (metrics, inspections) across dedicated TCP connections, preventing bulk operations from\ndelaying user-affecting control plane traffic.\n\n## Dialing Identity Forwarded to Hosting SDK\n\nThe identity ID and name of the dialing client are now forwarded to the hosting SDK when a circuit is\nestablished. This allows hosting applications to identify which identity initiated the connection,\nenabling identity-aware request handling on the server side. This will require SDK updates to add this\nto the API for hosting applications.\n\n## Controller-Initiated Control Channel Dials (BETA)\n\nControllers can now dial routers to establish control channels. Previously, routers were solely\nresponsible for dialing controllers. This feature is designed for deployments where one or more\ncontrollers are in a private network that routers cannot reach, but the controllers can dial out.\nA common scenario is an HA cluster where some controllers are publicly reachable and some are in\na private network. External routers connect to the public controllers normally, and the private\ncontrollers dial out to the routers.\n\n### Router Configuration\n\nRouters can configure one or more control channel listeners. Each listener specifies a bind address,\nan advertise address (reported to the controller), and optional groups for matching.\n\n```yaml\nctrl:\n  listeners:\n    - bind: tls://0.0.0.0:6262\n      advertise: tls://router.example.com:6262\n      groups:\n        - default\n```\n\nThe router is the authoritative source of ctrl channel listener information, similar to link\nlisteners. When a router connects to any controller, it reports its configured `ctrlChanListeners`\nand the controller data model is updated automatically. This means that in most deployments —\nwhere the router can reach at least one controller — no manual configuration of listener addresses\nis needed on the controller side.\n\nThe `ctrlChanListeners` field can also be set via the CLI for cases where a router cannot reach\nany controller and thus cannot initialize the data model itself:\n\n```bash\nziti edge update edge-router myRouter --bootstrap-ctrl-chan-listener 'tls://router.example.com:6262=group1,group2'\n```\n\nGroups default to `[\"default\"]` if not specified.\n\n### Controller Configuration\n\nThe controller dialer is disabled by default and must be explicitly enabled. When enabled, the\ncontroller will dial routers that have control channel listeners configured and are not already\nconnected.\n\n```yaml\nctrl:\n  dialer:\n    enabled: true\n    groups:\n      - default\n    dialDelay: 30s\n    minRetryInterval: 1s\n    maxRetryInterval: 5m\n    retryBackoffFactor: 1.5\n    fastFailureWindow: 5s\n    queueSize: 32\n    maxWorkers: 10\n```\n\n- `enabled` - Enables the controller dialer (default: `false`)\n- `groups` - List of groups to match against router listener groups (default: `[\"default\"]`)\n- `dialDelay` - Delay before the controller starts dialing after boot (default: `30s`)\n- `minRetryInterval` - Minimum backoff delay between dial retries (default: `1s`)\n- `maxRetryInterval` - Maximum backoff delay between dial retries (default: `5m`)\n- `retryBackoffFactor` - Multiplier applied to the retry delay on each failure, jittered +/- 0.5 (default: `1.5`)\n- `fastFailureWindow` - If a connection drops within this window after connecting, backoff continues rather than resetting (default: `5s`)\n- `queueSize` - Maximum number of pending dial jobs in the worker pool queue (default: `32`)\n- `maxWorkers` - Maximum number of concurrent dial workers (default: `10`)\n\nThe controller will only dial routers whose listener groups overlap with the controller's configured\ngroups. When a router advertises multiple ctrl channel listener addresses, the dialer rotates through\nthem on each failure so that an unreachable address does not block attempts to the others.\n\n### Metrics\n\nThe controller dialer worker pool exposes the following metrics under the `ctrl_channel.dialer` prefix:\n\n- `ctrl_channel.dialer.queue_size` - Current number of pending dial jobs in the queue\n- `ctrl_channel.dialer.worker_count` - Current number of dial worker goroutines\n- `ctrl_channel.dialer.busy_workers` - Number of workers currently executing a dial\n- `ctrl_channel.dialer.work_timer` - Timer tracking the duration of each dial attempt\n\n## SDK Inspection Support\n\nNew CLI commands have been added for inspecting SDK state, useful for diagnosing terminator and\nconnectivity issues.\n\n**Note:** SDK inspection requires SDK support. Currently only the Go SDK supports inspection,\nas of version 1.5.0. Other SDKs will need to add support before these commands can be used\nwith them.\n\n### `ziti fabric inspect sdk`\n\nRetrieves SDK context inspection data from identities connected to routers.\n\n```\nziti fabric inspect sdk <target-selector> <identity-id>\n```\n\nThe `<target-selector>` is a regex matching router IDs (use `.*` for all routers). The\n`<identity-id>` is the identity whose SDK context you want to inspect. The command returns\ndetailed state from the connected SDK instance, including active services, terminators, and\nconnection status.\n\n### `ziti agent tunnel dump-sdk`\n\nDumps SDK context information from a running `ziti tunnel` process via the IPC agent.\n\n```\nziti agent tunnel dump-sdk\n```\n\nReturns JSON-formatted inspection data for all SDK contexts registered in the tunnel process,\nincluding service listeners, connections, and terminator state.\n\n## Current Beta Features\n\n* Basic Permission System\n* Alert Events\n* Controller-Initiated Control Channel Dials\n\n## Component Updates and Bug Fixes\n\n* github.com/openziti/agent: [v1.0.31 -> v1.0.33](https://github.com/openziti/agent/compare/v1.0.31...v1.0.33)\n* github.com/openziti/channel/v4: [v4.2.28 -> v4.3.9](https://github.com/openziti/channel/compare/v4.2.28...v4.3.9)\n    * [Issue #235](https://github.com/openziti/channel/issues/235) - Bump allowed hello message headers size to 16k from 4k\n    * [Issue #228](https://github.com/openziti/channel/issues/228) - Ensure that Underlay never return nil on MultiChannel\n    * [Issue #226](https://github.com/openziti/channel/issues/226) - Allow specifying a minimum number of underlays for a channel, regardless of underlay type\n    * [Issue #225](https://github.com/openziti/channel/issues/225) - Add ChannelCreated to the UnderlayHandler API to allow handlers to be initialized with the channel before binding\n    * [Issue #224](https://github.com/openziti/channel/issues/224) - Update the underlay dispatcher to allow unknown underlay types to fall through to the default\n    * [Issue #222](https://github.com/openziti/channel/issues/222) - Allow injecting the underlay type into messages\n\n* github.com/openziti/edge-api: [v0.26.47 -> v0.27.5](https://github.com/openziti/edge-api/compare/v0.26.47...v0.27.5)\n    * [Issue #175](https://github.com/openziti/edge-api/issues/175) - ctrlChanListeners should have x-omit-empty: false attribute\n    * [Issue #170](https://github.com/openziti/edge-api/issues/170) - Add preferredLeader flag to controllers\n    * [Issue #167](https://github.com/openziti/edge-api/issues/167) - Add ctrlChanListeners to router types\n    * [Issue #164](https://github.com/openziti/edge-api/issues/164) - Add permissions list to identity\n\n* github.com/openziti/foundation/v2: [v2.0.72 -> v2.0.89](https://github.com/openziti/foundation/compare/v2.0.72...v2.0.89)\n    * [Issue #472](https://github.com/openziti/foundation/issues/472) - Add support for multi-bit set/get to AtomicBitSet\n    * [Issue #464](https://github.com/openziti/foundation/issues/464) - Add support for -pre in versions\n    * [Issue #455](https://github.com/openziti/foundation/issues/455) - Correctly close goroutine pool when external close is signaled\n    * [Issue #452](https://github.com/openziti/foundation/issues/452) - Goroutine pool with a min worker count of 1 can drop to 0 workers due to race condition\n\n* github.com/openziti/identity: [v1.0.111 -> v1.0.127](https://github.com/openziti/identity/compare/v1.0.111...v1.0.127)\n    * [Issue #68](https://github.com/openziti/identity/issues/68) - Shutdown file watcher when stopping identity watcher\n\n* github.com/openziti/metrics: [v1.4.2 -> v1.4.4](https://github.com/openziti/metrics/compare/v1.4.2...v1.4.4)\n    * [Issue #58](https://github.com/openziti/metrics/issues/58) - Add GaugeFloat64 support\n    * [Issue #56](https://github.com/openziti/metrics/issues/56) - underlying resources of reference counted meters are not cleaned up when reference count hits zero\n\n* github.com/openziti/runzmd: [v1.0.80 -> v1.0.90](https://github.com/openziti/runzmd/compare/v1.0.80...v1.0.90)\n* github.com/openziti/sdk-golang: [v1.2.3 -> v1.5.3](https://github.com/openziti/sdk-golang/compare/v1.2.3...v1.5.3)\n    * [Issue #887](https://github.com/openziti/sdk-golang/issues/887) - Fix listener manager cleanup\n    * [Issue #886](https://github.com/openziti/sdk-golang/issues/886) - When controller is busy during service refresh, backoff and retry instead of falling back to full refresh\n    * [Issue #885](https://github.com/openziti/sdk-golang/issues/885) - Only compare relevant service fields when looking for changes\n    * [Issue #884](https://github.com/openziti/sdk-golang/issues/884) - Add deadline for bind establishment\n    * [Issue #883](https://github.com/openziti/sdk-golang/issues/883) - Router level listener can be left open if multi-listener closes during listener establishment\n    * [Issue #877](https://github.com/openziti/sdk-golang/issues/877) - Handle differences in xgress eof/end-of-circuit handling by adding a capabilities exchange\n    * [Issue #832](https://github.com/openziti/sdk-golang/issues/832) - Fuzz session refresh timers\n    * [Issue #879](https://github.com/openziti/sdk-golang/issues/879) - Return the connId in inspect response\n    * [Issue #878](https://github.com/openziti/sdk-golang/issues/878) - Fix responses from rx goroutines\n    * [Issue #874](https://github.com/openziti/sdk-golang/issues/874) - Add inspect support at the context level\n    * [Issue #871](https://github.com/openziti/sdk-golang/issues/871) - Make SDK better at sticking to MaxTerminator terminators\n    * [Issue #708](https://github.com/openziti/sdk-golang/issues/708) - Support for Go's built-in context in Dial methods\n    * [Issue #860](https://github.com/openziti/sdk-golang/issues/860) - Make the dialing identity's id and name available on dialed connections\n    * [Issue #857](https://github.com/openziti/sdk-golang/issues/857) - Use new error code and retry hints to correctly react to terminator errors\n    * [Issue #847](https://github.com/openziti/sdk-golang/issues/847) - Ensure the initial version check succeeds, to ensure we don't legacy sessions on ha or oidc-enabled controllers\n    * [Issue #824](https://github.com/openziti/sdk-golang/pull/824) - release notes and hard errors on no TOTP handler breaks partial auth events\n    * [Issue #818](https://github.com/openziti/sdk-golang/issues/818) - Full re-auth should not clear services list, as that breaks the on-change logic\n    * [Issue #817](https://github.com/openziti/sdk-golang/issues/817) - goroutines can get stuck when iterating over randomized HA controller list\n    * [Issue #736](https://github.com/openziti/sdk-golang/issues/736) - Migrate from github.com/mailru/easyjson\n    * [Issue #813](https://github.com/openziti/sdk-golang/issues/813) - SDK doesn't stop close listener when it detects that a service being hosted gets deleted\n    * [Issue #811](https://github.com/openziti/sdk-golang/issues/811) - Credentials are lost when explicitly set\n    * [Issue #807](https://github.com/openziti/sdk-golang/issues/807) - Don't send close from rxer to avoid blocking\n    * [Issue #800](https://github.com/openziti/sdk-golang/issues/800) - Tidy create service session logging\n\n* github.com/openziti/secretstream: [v0.1.39 -> v0.1.48](https://github.com/openziti/secretstream/compare/v0.1.39...v0.1.48)\n* github.com/openziti/storage: [v0.4.26 -> v0.4.39](https://github.com/openziti/storage/compare/v0.4.26...v0.4.39)\n    * [Issue #122](https://github.com/openziti/storage/issues/122) - StringFuncNode has incorrect nil check, allowing panic\n    * [Issue #120](https://github.com/openziti/storage/issues/120) - Change post tx commit constraint handling order\n    * [Issue #119](https://github.com/openziti/storage/issues/119) - Add ContextDecorator API\n\n* github.com/openziti/transport/v2: [v2.0.188 -> v2.0.214](https://github.com/openziti/transport/compare/v2.0.188...v2.0.214)\n    * [Issue #31](https://github.com/openziti/transport/issues/31) - ipv6 Transport Address Parsing\n    * [Issue #149](https://github.com/openziti/transport/issues/149) - Archive transwarp code\n\n* github.com/openziti/xweb/v3: [v2.3.4 -> v3.0.4](https://github.com/openziti/xweb/compare/v2.3.4...v3.0.4)\n    * [Issue #32](https://github.com/openziti/xweb/issues/32) - watched identities sometimes don't reload when changed\n\n* github.com/openziti/go-term-markdown: v1.0.1 (new)\n* github.com/openziti/ziti/v2: [v1.6.8 -> v2.0.0](https://github.com/openziti/ziti/compare/v1.6.8...v2.0.0)\n    * [Issue #3674](https://github.com/openziti/ziti/issues/3674) - Update to Go 1.26\n    * [Issue #3496](https://github.com/openziti/ziti/issues/3496) - MFA TOTP Enrollment During OIDC Authentication Does Not Work\n    * [Issue #3609](https://github.com/openziti/ziti/issues/3609) - Stabilize terminator creation test for 2.0\n    * [Issue #3648](https://github.com/openziti/ziti/issues/3648) - tunnel: myCopy logs router ID as circuitId, causing misleading debug output\n    * [Issue #3658](https://github.com/openziti/ziti/issues/3658) - Raft cluster join fails with \"hello message too big\" when using long hostnames\n    * [Issue #3626](https://github.com/openziti/ziti/issues/3626) - controller: overlay bind point produces malformed URL in /versions apiBaseUrls\n    * [Issue #3635](https://github.com/openziti/ziti/issues/3635) - Allow controllers to dial routers to support more topologies\n    * [Issue #3607](https://github.com/openziti/ziti/issues/3607) - linux installer not upgradable from v1\n    * [Issue #3650](https://github.com/openziti/ziti/issues/3650) - Reroute doesn't proactively clean up orphaned route entries\n    * [Issue #3571](https://github.com/openziti/ziti/issues/3571) - Ensure 2.0 backwards compatibility with 1.6 and 1.5 using the smoketest\n    * [Issue #3636](https://github.com/openziti/ziti/issues/3636) - Adaptive rate limiter should use success rate rather than queue position\n    * [Issue #3600](https://github.com/openziti/ziti/issues/3600) - Add a preferredLeader flag, allow selected nodes to be preferred for raft leader, if they're available\n    * [Issue #3642](https://github.com/openziti/ziti/issues/3642) - Use xgress_common.Connection type for xgress_transport and xgress_proxy\n    * [Issue #3597](https://github.com/openziti/ziti/issues/3597) - Enable OIDC API by default\n    * [Issue #3624](https://github.com/openziti/ziti/issues/3624) - Multi-underlay control channel doesn't correctly handle lack of group secret on non-grouped underlays\n    * [Issue #3613](https://github.com/openziti/ziti/issues/3613) - Initializing cluster from existing db using `db:` config settings results in panic\n    * [Issue #3620](https://github.com/openziti/ziti/issues/3620) - Controller control channel heartbeats config parsing was erroneously nested under options parsing\n    * [Issue #3619](https://github.com/openziti/ziti/issues/3619) - Router connect events full sync interval was using min/max values meant for the batch interval\n    * [Issue #3618](https://github.com/openziti/ziti/issues/3618) - Router interfaceDiscovery.minReportInterval value was being set to checkInterval\n    * [Issue #3617](https://github.com/openziti/ziti/issues/3617) - Transit router disabled flag not passed through raft command structure\n    * [Issue #3333](https://github.com/openziti/ziti/issues/3333) - Updb user-lockout triggered by successful login attempts\n    * [Issue #3599](https://github.com/openziti/ziti/issues/3599) - Add gap detection and handling to router data model\n    * [Issue #3356](https://github.com/openziti/ziti/issues/3356) - Add WWW-Authenticate Headers\n    * [Issue #3074](https://github.com/openziti/ziti/issues/3074) - Dynamic cost range is too limited\n    * [Issue #3558](https://github.com/openziti/ziti/issues/3558) - terminator cost increased on egress dial success, not on circuit completion\n    * [Issue #3556](https://github.com/openziti/ziti/issues/3556) - global circuit costs not cleared when terminator is deleted\n    * [Issue #3557](https://github.com/openziti/ziti/issues/3557) - costing calculation for the weighted terminator selection strategy  is incorrect\n    * [Issue #2512](https://github.com/openziti/ziti/issues/2512) - Return circuit ID and error in dial failures\n    * [Issue #3569](https://github.com/openziti/ziti/issues/3569) - Version 2.0+ routers should not connect to controllers which do not support JWT formatted legacy sessions\n    * [Issue #3565](https://github.com/openziti/ziti/issues/3565) - Link dialer save 'is first conn' true, so all dials claim to be first, causing potential race condition\n    * [Issue #3550](https://github.com/openziti/ziti/issues/3550) - Support multi-underlay control channels\n    * [Issue #3535](https://github.com/openziti/ziti/issues/3535) - Remove the legacy xgress_edge_tunnel implementation\n    * [Issue #3547](https://github.com/openziti/ziti/issues/3547) - Add support for sending the dialing identity id and name to the hosting sdk\n    * [Issue #3541](https://github.com/openziti/ziti/issues/3541) - Remove option to disable the router data model in the controller\n    * [Issue #3540](https://github.com/openziti/ziti/issues/3540) - Handle UDP difference between proxy and tproxy implementations\n    * [Issue #3527](https://github.com/openziti/ziti/issues/3527) - ER/T UDP tunnels keep closed connections for 30s, preventing potential new good connections in that time\n    * [Issue #3526](https://github.com/openziti/ziti/issues/3526) - ER/T half-close logic is incorrect\n    * [Issue #3524](https://github.com/openziti/ziti/issues/3524) - Provide more error context to SDKs for terminator errors\n    * [Issue #3509](https://github.com/openziti/ziti/issues/3509) - Enforce policy on the router for oidc sessions, by closing open circuits and terminators when service access is lost\n    * [Issue #3531](https://github.com/openziti/ziti/issues/3531) - Remove created/updated/deleted terminator events. Obsoleted by entity change events.\n    * [Issue #3532](https://github.com/openziti/ziti/issues/3532) - Removed deprecated create identity <type> subcommands\n    * [Issue #3521](https://github.com/openziti/ziti/issues/3521) - Cleanup CLI to remove calls to os.Exit to be embed friendlier\n    * [Issue #3516](https://github.com/openziti/ziti/issues/3516) - Remove support for create terminator v1\n    * [Issue #3512](https://github.com/openziti/ziti/issues/3512) - Remove legacy link management code from the controller\n    * [Issue #3511](https://github.com/openziti/ziti/issues/3511) - router proxy mode fails to resolve interface if binding is 0.0.0.0\n    * [Issue #3503](https://github.com/openziti/ziti/issues/3503) - Allow routers to request current cluster membership information\n    * [Issue #3501](https://github.com/openziti/ziti/issues/3501) - Get cluster membership information from raft directly, rather than trying to cache it in the DB\n    * [Issue #3500](https://github.com/openziti/ziti/issues/3500) - Set a router data model timeline when initializing a new HA setup, rather than letting it stay blank\n    * [Issue #3504](https://github.com/openziti/ziti/issues/3504) - Reduce router data model full state updates\n    * [Issue #3492](https://github.com/openziti/ziti/pull/3492) - Bump openziti/ziti-console-assets from 3.12.9 to 4.0.0 in /dist/docker-images/ziti-controller in the all group\n    * [Issue #3484](https://github.com/openziti/ziti/issues/3484) - router ctrl channel handler for handling cluster changes has an initialization race condition\n    * [Issue #3477](https://github.com/openziti/ziti/issues/3477) - Optionally enable model changes triggered by login to be non-blocking and to be droppable if the system is under load\n    * [Issue #3473](https://github.com/openziti/ziti/issues/3473) - Enable tls handshake rate limiter by default and tweak default values.\n    * [Issue #3471](https://github.com/openziti/ziti/issues/3471) - Go tunneler is ignoring host config MaxConnections\n    * [Issue #3469](https://github.com/openziti/ziti/issues/3469) - Only send model updates on resubscribe if the RDM index has advanced\n    * [Issue #2573](https://github.com/openziti/ziti/issues/2573) - An edge router in a tight restart loop causes a resource leak on routers to which it connects.\n    * [Issue #3430](https://github.com/openziti/ziti/issues/3430) - Add permissions list to identity\n    * [Issue #2109](https://github.com/openziti/ziti/issues/2109) - Add Edge Management Read Only Capability\n    * [Issue #3435](https://github.com/openziti/ziti/issues/3435) - Add edge management API permissions by entity type and action\n    * [Issue #3441](https://github.com/openziti/ziti/issues/3441) - Update router connection tracker to interrogate active connections\n    * [Issue #3451](https://github.com/openziti/ziti/issues/3451) - ci - compare only stable releases when promoting\n    * [Issue #3437](https://github.com/openziti/ziti/issues/3437) - SDK OIDC token updates to routers should return an error if invalid\n    * [Issue #3348](https://github.com/openziti/ziti/issues/3348) - Unable to clear/reset the \"tags\" property on an entity to an empty object\n    * [Issue #3452](https://github.com/openziti/ziti/issues/3452) - `ziti agent cluster add` has bad behavior if the add address doesn't match the advertise address\n    * [Issue #3410](https://github.com/openziti/ziti/issues/3410) - Consolidate fabric REST API code with edge management and edge client code\n    * [Issue #3425](https://github.com/openziti/ziti/issues/3425) - RDM not properly responding to tunneler enabled flag changes\n    * [Issue #3420](https://github.com/openziti/ziti/issues/3420) - The terminator id cache uses the same id for all terminators in a host.v2 config, resulting in a single terminator\n    * [Issue #3419](https://github.com/openziti/ziti/issues/3419) - When using the router data model, precedence specified on the per-service identity mapping are incorrectly interpreted\n    * [Issue #3318](https://github.com/openziti/ziti/issues/3318) - Terminator creation seems to slow exponentially as the number of terminators rises from 10k to 20k to 40k\n    * [Issue #3407](https://github.com/openziti/ziti/issues/3407) - The CLI doesn't properly pass JWT authentication information to websocket endpoints\n    * [Issue #3359](https://github.com/openziti/ziti/issues/3359) - Ensure router data model subscriptions have reasonable performance and will scale\n    * [Issue #3354](https://github.com/openziti/ziti/issues/3354) - SDK/ENV Info from SDKs is not distributed in HA\n    * [Issue #3381](https://github.com/openziti/ziti/issues/3381) - the fabric service REST apis are missing the maxIdleTime property\n    * [Issue #3382](https://github.com/openziti/ziti/issues/3382) - Legacy service sessions generated pre-1.7.x are incompatible with v1.7.+ and need to be cleared\n    * [Issue #3339](https://github.com/openziti/ziti/issues/3339) - get router ctrl.endpoint from ctrls claim in JWT\n    * [Issue #3378](https://github.com/openziti/ziti/issues/3378) - login with file stopped working\n    * [Issue #3349](https://github.com/openziti/ziti/issues/3349) - UPDB OIDC login returns wrong content type\n    * [Issue #2324](https://github.com/openziti/ziti/issues/2324) - Add Ext-JWT/OIDC enrollment\n    * [Issue #3346](https://github.com/openziti/ziti/issues/3346) - Fix confusing attempt logging\n    * [Issue #3337](https://github.com/openziti/ziti/issues/3337) - Router reports \"no xgress edge forwarder for circuit\"\n    * [Issue #3345](https://github.com/openziti/ziti/issues/3345) - Clean up connect events tests and remove global XG registry\n    * [Issue #3264](https://github.com/openziti/ziti/issues/3264) - Allow routers to generate alert events in cases of service misconfiguration\n    * [Issue #3321](https://github.com/openziti/ziti/issues/3321) - Health Check API missing base path on discovery endpoint\n    * [Issue #3323](https://github.com/openziti/ziti/issues/3323) - router/tunnel static services fail to bind unless new param protocol is defined\n    * [Issue #3309](https://github.com/openziti/ziti/issues/3309) - Detect link connections meant for another router\n    * [Issue #3286](https://github.com/openziti/ziti/issues/3286) - edge-api binding doesn't have the correct path on discovery endpoints\n    * [Issue #3297](https://github.com/openziti/ziti/issues/3297) - stop promoting hotfixes downstream\n    * [Issue #3295](https://github.com/openziti/ziti/issues/3295) - make ziti tunnel service:port pairs optional\n    * [Issue #3291](https://github.com/openziti/ziti/issues/3291) - replace decommissioned bitnami/kubectl\n    * [Issue #3277](https://github.com/openziti/ziti/issues/3277) - Router can deadlock on closing a connection if the incoming data channel is full\n    * [Issue #3269](https://github.com/openziti/ziti/issues/3269) - Add host-interfaces config type\n    * [Issue #3258](https://github.com/openziti/ziti/issues/3258) - Add config type proxy.v1 so proxies can be defined dynamically for the ER/T\n    * [Issue #3259](https://github.com/openziti/ziti/issues/3259) - Interfaces config type not added due to wrong name\n    * [Issue #3265](https://github.com/openziti/ziti/issues/3265) - Forwarding errors should log at debug, since they are usual part of circuit teardown\n    * [Issue #3261](https://github.com/openziti/ziti/issues/3261) - ER/T dialed xgress connections may only half-close when peer is fully closed\n    * [Issue #3207](https://github.com/openziti/ziti/issues/3207) - Allow router embedders to customize config before start\n\n\n","mentions_count":1},{"url":"https://api.github.com/repos/openziti/ziti/releases/295901158","assets_url":"https://api.github.com/repos/openziti/ziti/releases/295901158/assets","upload_url":"https://uploads.github.com/repos/openziti/ziti/releases/295901158/assets{?name,label}","html_url":"https://github.com/openziti/ziti/releases/tag/v2.0.0-pre4","id":295901158,"author":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"node_id":"RE_kwDODVFMN84Roxfm","tag_name":"v2.0.0-pre4","target_commitish":"main","name":"v2.0.0-pre4","draft":false,"immutable":false,"prerelease":true,"created_at":"2026-03-11T22:08:04Z","updated_at":"2026-03-11T22:15:35Z","published_at":"2026-03-11T22:15:35Z","assets":[{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/371856466","id":371856466,"node_id":"RA_kwDODVFMN84WKhRS","name":"checksums.sha256.txt","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"text/plain; charset=utf-8","state":"uploaded","size":790,"digest":"sha256:47b5093fa5f13f58e06029f63f9686c8d1a4933044b5e1c914a2f4ce212bde23","download_count":3,"created_at":"2026-03-11T22:15:33Z","updated_at":"2026-03-11T22:15:33Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre4/checksums.sha256.txt"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/371856463","id":371856463,"node_id":"RA_kwDODVFMN84WKhRP","name":"sbom-v2.0.0-pre4.spdx.json","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/json","state":"uploaded","size":999351,"digest":"sha256:b87a0fde1b5d68cd43b783bccca7a52b2026ff864884cd785bb8b088635c396a","download_count":3,"created_at":"2026-03-11T22:15:33Z","updated_at":"2026-03-11T22:15:33Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre4/sbom-v2.0.0-pre4.spdx.json"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/371856464","id":371856464,"node_id":"RA_kwDODVFMN84WKhRQ","name":"source-v2.0.0-pre4.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":3758333,"digest":"sha256:2e33d5601d89bd38f4b2044429326cca822686413555d12bc0bbb5ec20082122","download_count":3,"created_at":"2026-03-11T22:15:33Z","updated_at":"2026-03-11T22:15:33Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre4/source-v2.0.0-pre4.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/371856442","id":371856442,"node_id":"RA_kwDODVFMN84WKhQ6","name":"ziti-darwin-amd64-2.0.0-pre4.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":53594579,"digest":"sha256:97bb5b8c370a293d504e87b109004c624fe0d2b8857de9f88b3822f84880c7f2","download_count":2,"created_at":"2026-03-11T22:15:30Z","updated_at":"2026-03-11T22:15:33Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre4/ziti-darwin-amd64-2.0.0-pre4.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/371856444","id":371856444,"node_id":"RA_kwDODVFMN84WKhQ8","name":"ziti-darwin-arm64-2.0.0-pre4.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":49935019,"digest":"sha256:532b2d9b22a0cdff5eaec787e391c9eb6629c84048104346115a0e291d2c5495","download_count":3,"created_at":"2026-03-11T22:15:30Z","updated_at":"2026-03-11T22:15:33Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre4/ziti-darwin-arm64-2.0.0-pre4.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/371856445","id":371856445,"node_id":"RA_kwDODVFMN84WKhQ9","name":"ziti-linux-amd64-2.0.0-pre4.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":54909248,"digest":"sha256:219c7d07d664a0bd9344e2485040bbffbab23b05db4b31506d8e64f724066d44","download_count":3,"created_at":"2026-03-11T22:15:30Z","updated_at":"2026-03-11T22:15:33Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre4/ziti-linux-amd64-2.0.0-pre4.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/371856446","id":371856446,"node_id":"RA_kwDODVFMN84WKhQ-","name":"ziti-linux-arm-2.0.0-pre4.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":51379330,"digest":"sha256:a3b8c13312872168d7b6c98637ac2826bf78760314479f64993f82519a93eeef","download_count":4,"created_at":"2026-03-11T22:15:30Z","updated_at":"2026-03-11T22:15:33Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre4/ziti-linux-arm-2.0.0-pre4.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/371856443","id":371856443,"node_id":"RA_kwDODVFMN84WKhQ7","name":"ziti-linux-arm64-2.0.0-pre4.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":51421578,"digest":"sha256:14610639cb814addb588485fd2d60828590ffe92cd78cf515af84d1ab4c2e6db","download_count":3,"created_at":"2026-03-11T22:15:30Z","updated_at":"2026-03-11T22:15:33Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre4/ziti-linux-arm64-2.0.0-pre4.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/371856462","id":371856462,"node_id":"RA_kwDODVFMN84WKhRO","name":"ziti-windows-amd64-2.0.0-pre4.zip","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/zip","state":"uploaded","size":44396031,"digest":"sha256:c1318b082b2bba2c639c725f238bc9726a430a55ce605e65b0d1413f9060fe7d","download_count":4,"created_at":"2026-03-11T22:15:33Z","updated_at":"2026-03-11T22:15:34Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre4/ziti-windows-amd64-2.0.0-pre4.zip"}],"tarball_url":"https://api.github.com/repos/openziti/ziti/tarball/v2.0.0-pre4","zipball_url":"https://api.github.com/repos/openziti/ziti/zipball/v2.0.0-pre4","body":"# Release 2.0.0\n\n## What's New\n\nThis is the next major version release of OpenZiti, following the 1.0 release in April 2024. \nOf particular note is that HA controllers are now considered ready for general use. \nThis release also introduces a new permissions model, OIDC/JWT token-based enrollment, \nclustering performance improvements, and a number of other features and fixes. Because \nsome of these changes are not backwards compatible with older routers, we're marking this \nas a major version bump.\n\n### HA Controllers are now considered ready for general use\n\nThis is a pretty big milestone and marks the completion of work that's been ongoing for a couple of years.\nThe HA work has brought with it some notable changes. Authentication now uses JWTs by default. Using JWTs\nmeans the controllers don't need to store session and propagate them to the routers. This removes a bottleneck\nfrom the network and allows the load to be more easily distributed among controllers and routers.\n\nTo support distributed authentication, the routers now get a bespoke version of the data model. In addition\nto enabling distributed authentication this will allow us to remove the need for service polling and further \nreduce the load on controllers in the future.\n\n### Router Compatibility\n\nRelated to the JWT work, routers with version 2.+ will only work with controllers that are version 2.+. This means\nto upgrade your network, controllers should be upgraded first. Routers can then be upgraded individually.\n\n2.x routers should still work fine with older router versions.\n\nWe try very hard to avoid breaking changes like this, but sometimes the engineering trade-offs lead there. This change\nwas first made in the 1.7 release. That release has not been marked stable, and we have no plans to do so, because\nof the backwards incompatibility. \n\nIf you need to support in the 1.6.12+ range, see the section \"OIDC enabled by default\".\n\n### New Permissions Model (BETA)\n\nAs one feature goes out of beta, another arrives into beta. This release introduces a new permissions system\nfor more fine grained control to the management API. It's not expected to change, but may do so based on feedback\nfrom users.\n\n### Updated Release Process\n\nWe have moved to creating `-preN` releases for major and minor versions. This way we can put out release candidates,\nor feature previews and put them through internal testing and let interested folks from the community try them out.\nThen, when we're ready, we can run the full validation suite against the last pre-release and retag it. \n\nPatch releases won't have `-preN` and should contain only high priority bug fixes.\n\n### Deprecation Cleanup\n\nSince we already have a breaking change, we're removing some other backwards compatibility code.\n\n* Controller managed links \n    * Router managed links were introduced in v0.30.0. \n    * If you're upgrading from an older versions, you'll want to upgrade to the latest 1.x release before jumping to 2.x\n    * Github tracking issue: https://github.com/openziti/ziti/issues/3512\n* `ziti edge create identity <type>`\n    * Identity types other than router were removed in v0.30.2\n    * The `type` can be dropped from the CLI command\n    * Github tracking issue: https://github.com/openziti/ziti/issues/3532\n* Terminator create/update/delete events\n    * These have been superseded by entity change events, which also have create/update/delete events for terminators\n    * Entity change events were introduced in v0.28.0\n    * Github tracking issue: https://github.com/openziti/ziti/issues/3531\n* `xgress_edge_tunnel` v1\n    * This is the first implementation of the tunneler in edge-router code (ER/T) which used legacy api sessions and services\n    * The v2 version uses the router data model and was introduced in v0.30.x\n    * Github tracking issue: https://github.com/openziti/ziti/issues/3516\n\n### Legacy Session Deprecation\n\nOIDC sessions are now preferred. They are the default, or will become the default for SDKs and tunnelers. They are also required\nwhen running HA. Legacy API and service session are now deprecated and will be removed in the OpenZiti v3.0.0 release. \n\n### Additional Features\n\n* Controllers can now optionally bind APIs using an OpenZiti identity\n* `ziti edge login` now supports the `--network-identity` flag to authenticate and establish connections through the Ziti overlay network\n* `ziti edge login` now supports using a bearer token with `--token` for authentication. The token is expected to be\n  provided as just the JWT, not with the \"Bearer \" prefix\n* Identity configuration can now be loaded from files or environment variables for flexible deployment scenarios\n* Identities can now be provisioned just-in-time through OIDC/JWT token-based enrollment\n* Multiple model updates can now be in-flight at the same time, improving clustering performance\n* Authentication-related model updates can now be non-blocking and even dropped if the system is too busy\n* Routers now provide more error context to SDKs for terminator errors, enabling better retry behavior\n* New `proxy.v1` config type for dynamic service proxies (originally released in 1.7.0)\n* New alert event type for surfacing operational issues to network operators - Beta (originally released in 1.7.0)\n* New Azure Service Bus event sink for streaming controller events, contributed by @ffaraone (originally released in 1.7.0)\n* Bundled ZAC upgraded to 4.0\n* Build updated to Go 1.25\n* CLI cleaned up to remove calls to `os.Exit`, making it more friendly for embedding\n* OIDC is now enabled by default\n* Controller Edge APIs now return `WWW-Authenticate` response headers on `401 Unauthorized` responses, giving clients actionable information about which auth methods are accepted and what went wrong\n* HA Controllers can be marked as 'preferredLeader' via config\n* Dynamic cost range for smart routing expanded beyond the previous 64K limit\n* Dial failures now return the circuit ID and error information for easier debugging\n* Router-to-controller control channels now support multiple underlays with priority-based message routing\n* The dialing identity's ID and name are now forwarded to the hosting SDK\n* Controllers can now dial routers to establish control channels, enabling connectivity when routers are behind firewalls (Beta)\n\n## Basic Permission System (BETA)\n\nAdded a basic permission system that allows more control over identity access to controller management API operations. \nThis replaces the previous binary admin/non-admin model with a more flexible permission system.\n\n**NOTE:** This feature is in BETA, primarily so we can get feedback on which permissions make sense. The implementation is unlikely to change\nbut the set of exposed permissions may grow, shrink or change based on user feedback. \n\n### Permission Model\n\nThe permission system supports three levels of authorization:\n\n  1. **Global Permissions**: System-wide access levels\n     - `admin` - Full access to all operations. This is still controlled by the `isAdmin` flag on identity\n     - `admin_readonly` - Read-only access to all resources except debugging facilities inspect and validate\n\n  2. **Entity-Level Permissions**: Full CRUD access to specific entity types\n     - Granting an entity-level permission (e.g., `service`) provides complete create, read, update, and delete access for that entity type\n\n  3. **Action-Level Permissions**: Specific operation access on entity types\n     - Fine-grained control using the pattern `<entity>.<action>` (e.g., `service.read`, `identity.update`)\n     - Supports `create`, `read`, `update`, and `delete` actions per entity type\n\n### Supported Entity Permissions\n\nThe following entity-level permissions are available:\n\n- `auth-policy` - Authentication policy management\n- `ca` - Certificate Authority management\n- `config` - Configuration management\n- `config-type` - Configuration type management\n- `edge-router-policy` - Edge router policy management\n- `enrollment` - Enrollment management\n- `external-jwt-signer` - External JWT signer management\n- `identity` - Identity management\n- `posture-check` - Posture check management\n- `router` - Edge and transit router management\n- `service` - Service management\n- `service-policy` - Service policy management\n- `service-edge-router-policy` - Service edge router policy management\n- `terminator` - Terminator management\n- `ops` - Operational resources (API sessions, sessions, circuits, links, inspect and validate)\n\n### Permission Assignment\n\nPermissions are assigned to identities via the `permissions` field in the identity resource. Multiple permissions can be granted to a single identity, and permissions are additive.\n\n### Cross-Entity Operations\n\nListing related entities through an entity's endpoints requires appropriate permissions for the related entity type. For example:\n- Listing services for a service-policy requires `service.read` permission\n- Listing identities for an edge-router-policy requires `identity.read` permission\n- Listing configs for a service requires `config.read` permission\n\n**NOTE:** \nMore permissions than expected may be required when performing actions through the CLI or ZAC. Take for example, when an identity \nhas `config.create` and is attempting to create a new config. The CLI may fail if the identity doesn't have `config-type.read`\nas well because it will need to look up the config type id that corresponds to the given config type name.\n\nSimilar cross entity read permissions may be required when creating services.\n\n### Admin Protection\n\nNon-admin identities cannot:\n- Create identities with the `isAdmin` flag\n- Create identities with any permissions granted\n- Modify admin-related fields on existing identities\n- Update or delete admin identities\n- Grant permissions to identities\n\nThese protections ensure that privilege escalation is prevented and admin access remains controlled.\n\n\n## Binding Controller APIs With Identity\n\nController APIs can now be bound to an OpenZiti overlay network identity, allowing secure communication through\nthe Ziti network. This is useful for scenarios where you want to expose controller APIs only through the overlay\nnetwork rather than on a standard network interface.\n\n### Configuration Structure\n\nA standard `bindPoint` configuration looks like this:\n```text\n    bindPoints:\n      - interface: 127.0.0.1:18441\n        address: 127.0.0.1:18441\n```\n\nTo bind controller APIs to an OpenZiti identity, add an additional `identity` block to your `bindPoints`. The\nidentity configuration specifies where to load the Ziti identity file and which service to bind it to:\n\n```text\n    bindPoints:\n      - interface: 127.0.0.1:18441\n        address: 127.0.0.1:18441\n      - identity:\n          file: \"c:/temp/ctrl.testing/ctrl.identity.json\"\n          service: \"mgmt\"\n```\n\n### Supported Configuration Options\n\n- `file`: Path to a Ziti identity JSON file containing the controller's identity and enrollment certificate\n- `env`: Name of an environment variable containing a base64-encoded Ziti identity (alternative to `file`)\n- `service`: The name of the Ziti service to bind the controller API to\n\n### Using Environment Variables\n\nFor deployments where storing identity files on disk is not preferred, you can reference a base64-encoded\nidentity file from an environment variable. The environment variable should contain the base64-encoded contents\nof the identity JSON file.\n\nFor example, if an environment variable named `ZITI_CTRL_IDENTITY` contains a base64-encoded identity file:\n\n```text\n    bindPoints:\n      - interface: 127.0.0.1:18441\n        address: 127.0.0.1:18441\n      - identity:\n          env: ZITI_CTRL_IDENTITY\n          service: \"mgmt\"\n```\n\n### IPv6 Support\n\nBoth IPv4 and IPv6 addresses are supported for standard bind points. IPv6 addresses should be specified in bracket\nnotation with a port number:\n\n```text\n    bindPoints:\n      - interface: \"[::1]:18441\"\n        address: \"[::1]:18441\"\n      - identity:\n          file: \"/path/to/identity.json\"\n          service: \"mgmt\"\n```\n\n## CLI Enhancements for Identity-Based Connections\n\nThe `ziti edge login` command and REST client utilities have been enhanced to support identity-based connections\nthrough the Ziti overlay network.\n\n### New `--network-identity` Flag for `ziti edge login`\n\nThe `ziti edge login` command now includes a `--network-identity` flag that allows you to authenticate to a Ziti\ncontroller through the overlay network using a Ziti identity:\n\n```bash\nziti edge login https://ziti.mgmt.apis.local:1280 \\\n  --username myuser \\\n  --password mypass \\\n  --network-identity /path/to/identity.json\n```\n\nThis is useful when the controller is only accessible through the Ziti overlay network or when you want to ensure\nall communication to the controller flows through the overlay for security purposes.\n\n### Identity Resolution Order\n\nWhen establishing connections, identities are resolved in the following order:\n\n1. **Command-line flag**: The `--network-identity` flag takes precedence\n2. **Environment variable**: If `ZITI_CLI_NETWORK_ID` is set and contains a base64-encoded identity, it is used\n3. **Cached identity file**: If a network identity was saved from a previous login in the Ziti config directory, it may be used\n\nThis layered approach allows for flexibility in deployment scenarios:\n- Development: Use command-line flags for quick testing\n- Automation: Use environment variables in CI/CD pipelines\n- Production: Cache identities securely for repeated access\n\n#### Dialing Modes When Authenticating\n\nThe CLI supports two dialing modes:\n\n**Intercept-based Dialing (Default)**\nBy default, URLs are expected to leverage intercepts. Create a service with an appropriate intercept config and use\nthe intercept address when dialing. This is the standard mode for most use cases. For example, given a service with\nthe intercept `ziti.mgmt.apis.local`\n```bash\nziti edge login https://ziti.mgmt.apis.local:1280 \\\n  --username myuser \\\n  --password mypass \\\n  --network-identity /path/to/identity.json\n```\n\n**Identity-aware Dialing (Addressable Terminators)**\nTo support addressable terminators-based dialing, specify a user in the URL. This activates dial-by-identity\nfunctionality. The URL format should be `identity-to-dial@service-name-to-dial`. For example:\n```bash\nziti edge login https://my-identity@my-service:1280 \\\n  --username myuser \\\n  --password mypass \\\n  --network-identity /path/to/identity.json\n```\n\nIn this mode, the transport extracts the identity from the URL and uses it to establish a direct connection to\nthe specified service via the addressable terminator.\n\n\n## OIDC/JWT Token-based Enrollment\n\nOpenZiti now supports provisioning identities just-in-time through OIDC/JWT token enrollment. External identity \nproviders can be configured to allow identities to enroll using JWT tokens, with support for the resulting \nidentities to use certificate or token authentication.\n\n### External JWT Signer Configuration\n\nExternal JWT signers are configured via the Edge Management API to define enrollment behavior with the following new \nenrollment-specific properties:\n\n- **enrollToCertEnabled** - When enabled, identities can exchange a JWT token and a certificate signing request (CSR) \n        for a client certificate during enrollment. The certificate can then be used for standard certificate-based\n        authentication.\n\n- **enrollToTokenEnabled** - When enabled, identities can use a JWT token to enroll. The current token or future tokens\n        may be used for authentication.\n\n- **enrollNameClaimsSelector** - Specifies which JWT claim contains the identity name. Accepts a JSON pointer \n        (e.g., `/preferred_username`) or a simple property name (e.g., `preferred_username`, automatically converted to\n        `/preferred_username`). Defaults to `/sub` if not specified. The extracted value becomes the identity name in Ziti.\n\n- **enrollAttributeClaimsSelector** - Specifies which JWT claims to extract as identity attributes during enrollment.\n        Accepts a JSON pointer (e.g., `/roles`) or a simple property name (e.g., `roles`). Extracted attributes are \n        applied to the newly enrolled identity for use in authorization policies.\n\n- **enrollAuthPolicyId** - Specifies the authentication policy to apply to newly enrolled identities. This determines\n        what authentication methods are available for the identity post-enrollment.\n\nAdditionally the existing property named **claimsProperty** that specifies external id to match identities to:\n\n- now supports a JSON pointer (e.g., `/id`) or a simple property name (e.g., `id`)\n- is used to populate the `externalId` field of the identity\n\n### Enrollment Paths\n\n#### Certificate Enrollment (enrollToCertEnabled)\n\nWhen certificate enrollment is enabled, unauthenticated users can:\n\n1. Obtain a list of available IdPs from the public Edge Client API `GET /external-jwt-signers` endpoint, where \n   `enrollToCertEnabled` is set to `true`\n2. Obtain a JWT from the configured OIDC provider\n3. Generate a certificate signing request (CSR)\n4. Submit an enrollment request with the JWT and CSR\n5. Have their identity created in Ziti with attributes extracted from JWT claims\n6. Receive a signed client certificate for certificate-based authentication\n\n#### Token Enrollment (enrollToTokenEnabled)\n\nWhen token enrollment is enabled, unauthenticated users can:\n\n1. Obtain a list of available IdPs from the public Edge Client API `GET /external-jwt-signers` endpoint, where \n   `enrollToTokenEnabled` is set to `true`\n1. Obtain a JWT from the configured OIDC provider\n2. Submit an enrollment request with the JWT\n3. Have their identity created in Ziti with attributes extracted from JWT claims\n4. Receive a Ziti API token for token-based authentication\n\n### Edge Management API\n\nThe Edge Management API provides full CRUD operations for configuring external JWT signers:\n\n- `POST /external-jwt-signers` - Create a new external JWT signer with all configuration options\n- `GET /external-jwt-signers` - List all configured external JWT signers\n- `GET /external-jwt-signers/{id}` - Retrieve a specific signer configuration\n- `PUT /external-jwt-signers/{id}` - Update all fields of a signer\n- `PATCH /external-jwt-signers/{id}` - Partially update a signer\n- `DELETE /external-jwt-signers/{id}` - Delete a signer\n\n### Edge Client API\n\nThe Edge Client API exposes a reduced set of external JWT signer information for unauthenticated enrollment requests:\n\n- `GET /external-jwt-signers` - List available JWT signers with enrollment capabilities\n\nThe client API response includes the following fields for each signer:\n\n- `name` - Signer name\n- `externalAuthUrl` - URL where users obtain JWT tokens\n- `clientId` - OIDC client ID\n- `scopes` - Requested OIDC scopes\n- `openIdConfigurationUrl` - OIDC discovery endpoint\n- `audience` - Expected token audience\n- `targetToken` - Token type to use (ACCESS or ID)\n- **`enrollToCertEnabled`** - Flag indicating certificate enrollment is available\n- **`enrollToTokenEnabled`** - Flag indicating token enrollment is available\n\n### CLI Commands\n\n**Create an external JWT signer with enrollment options:**\n```\nziti edge controller create ext-jwt-signer <name> <issuer> \\\n  --jwks-endpoint <url> \\\n  --audience <audience> \\\n  --enroll-to-cert \\\n  --enroll-to-token=false \\\n  --enroll-name-claims-selector preferred_username \\\n  --enroll-attr-claims-selector roles \\\n  --enroll-auth-policy <policy-id-or-name>\n```\n\n**Update enrollment options on an existing signer:**\n```\nziti edge controller update ext-jwt-signer <name|id> \\\n  --enroll-to-cert \\\n  --enroll-auth-policy <policy-id-or-name>\n```\n\n**List external JWT signers:**\n```\nziti edge controller list ext-jwt-signers\n```\n\n## Clustering Performance Improvements\n\nIn previous releases, model updates were submitted to raft one at at time. This prevented \nraft from being efficient by allowing command batching. This release allows multiple \nmodel updates to be in-flight at the same time. \n\nNew Configuration Options\n\n1. Raft Apply Timeout (cluster.applyTimeout)\n\nLocation: Controller configuration file, under the cluster section\nType: Duration\nDefault: 5s\nDescription: Timeout for applying commands to the Raft distributed log. Commands that exceed this timeout will trigger adaptive rate limiter backoff.\n\nExample:\n```\ncluster:\n  applyTimeout: 10s\n```\n\n2. Cluster Rate Limiter Configuration (cluster.rateLimiter)\n\nA new adaptive rate limiter that controls the submission of commands to the Raft cluster. Unlike the existing command rate limiter, this specifically manages in-flight Raft operations with adaptive window sizing.\n\nConfiguration Structure:\n```\ncluster:\n  rateLimiter:\n    enabled: true\n    minSize: 5\n    maxSize: 250\n    timeout: 30s\n```\n\nSub-options:\n\n  - enabled (boolean)\n    - Default: true\n    - Description: Enable/disable adaptive rate limiting for Raft command submission\n  - minSize (integer)\n    - Default: 5\n    - Minimum: 1\n    - Description: Minimum window size for concurrent in-flight Raft operations\n  - maxSize (integer)\n    - Default: 250\n    - Description: Maximum window size for concurrent in-flight Raft operations. Must be >= minSize\n  - timeout (duration)\n    - Default: 30s\n    - Description: Time after which outstanding work is assumed to have failed if not marked completed\n\n3. Restart Self on Snapshot (cluster.restartSelfOnSnapshot)\n\nLocation: Controller configuration file, under cluster section\nType: Boolean\nDefault: false\nDescription: When true, the controller will automatically restart itself when restoring a snapshot to an initialized system. When false, the controller will exit with code 0, requiring external process management to restart it.\n\nExample:\n```\ncluster:\n    restartSelfOnSnapshot: true\n```\n\n### New Metrics\n\nThe adaptive rate limiter exposes three new metrics:\n\n  1. raft.rate_limiter.queue_size (gauge)\n    - Current number of operations queued/in-flight\n  2. raft.rate_limiter.work_timer (timer)\n    - Duration of rate-limited operations\n  3. raft.rate_limiter.window_size (gauge)\n    - Current adaptive window size\n\n## Rate Limiter Algorithm Improvements\n\nThe adaptive rate limiter tracker now uses a success rate metric to decide when to grow or shrink its\nconcurrency window, instead of using raw queue position. An exponentially decaying histogram tracks the\nratio of successes to backoffs. When the success rate exceeds a configurable threshold, the window is\ngrown by a multiplicative increase factor. When it falls below the threshold, the window is shrunk by a\nmultiplicative decrease factor. The check intervals for increase and decrease are independently configurable.\n\nThis approach produces smoother, more stable window adjustments under varying load, avoiding the\nover-aggressive shrinking that could occur when queue position alone was used as the signal.\n\nThis specific rate limiter implementation is used in three places:\n* **Controller TLS handshake rate limiting** - controls the rate of incoming TLS handshakes on the controller\n* **Raft command submission** - controls the rate of commands submitted to the Raft distributed log\n* **Router control channel rate limiting** - controls the rate of requests from the router to the controller\n\nNote: this work was done in advance of enabling the TLS handshake rate limiter by default. The TLS\nhandshake rate limiter is not yet enabled by default, but can be enabled via the `tls.rateLimiter`\nconfiguration section.\n\nNew configuration options (available under each `rateLimiter` section):\n\n  - successThreshold (float)\n    - Default: 0.9\n    - Description: Success rate threshold above which the window size will be increased and below which it will be decreased\n  - increaseFactor (float)\n    - Default: 1.02\n    - Description: Multiplier applied to the current window size when growing. Must be greater than 1\n  - decreaseFactor (float)\n    - Default: 0.9\n    - Description: Multiplier applied to the current window size when shrinking. Must be between 0 and 1\n  - increaseCheckInterval (integer)\n    - Default: 10\n    - Description: Number of successes between window size increase checks\n  - decreaseCheckInterval (integer)\n    - Default: 10\n    - Description: Number of backoffs between window size decrease checks\n\n## Background Processing for Identity Updates\n\nIdentity environment and authenticator updates that occur during authentication are now processed asynchronously in the background. \nThis prevents authentication requests from blocking when the system is under load, significantly improving resilience during thundering herd scenarios.\n\nWhen the background queue fills up, updates can be dropped as they will be refreshed on the next authentication attempt. \nThis allows the system to gracefully handle load spikes without impacting authentication performance.\n\nFor now, dropping entries when the queue fills will be disabled by default, but can be enabled, see below.\n\n### Configuration\n\nA new `command.background` configuration section controls the background processing behavior:\n\n```yaml\n  command:\n    background:\n      enabled: true           # Enable background processing (default: true)\n      queueSize: 1000        # Maximum queue size (default: 1000)\n      dropWhenFull: true     # Drop updates when queue is full (default: false)\n      delayThreshold: 50ms   # The threshold for how long updates are taking before starting to background updates\n```\n\nNote that the `commandRateLimiter` configuration section may instead be specified under `command` as `rateLimiter`.\n\nExample:\n\n```yaml\n  command:\n    background:\n      enabled: true\n      queueSize: 250\n      dropWhenFull: false\n      delayThreshold: 50ms\n    rateLimiter:\n      enabled:   true\n      maxQueued: 25\n```\n\nNote that if command rate limiter configuration is specified in both locations, the settings under `command` will take \nprecedence. The standalone `commandRateLimiter` section may be deprecated in the future.\n\n### Metrics\n\nWhen background processing is enabled, the following metrics are exposed:\n\n- command.background.queue_size - Current number of queued background tasks\n- command.background.worker_count - Current number of worker goroutines\n- command.background.busy_workers - Number of workers currently processing tasks\n- command.background.work_timer - Timer tracking background task execution (includes histogram, meter, and count)\n- command.background.dropped_entries - Count of dropped updates when queue is full (only when dropWhenFull is enabled)\n\n## New proxy.v1 Config Type\n\n*Originally released in 1.7.0*\n\nAdded support for dynamic service proxies with configurable binding and protocol options.\nThis allows Edge Routers and Tunnelers to create proxy endpoints that can forward traffic for Ziti services.\n\nThis differs from intercept.v1 in that intercept.v1 will intercept traffic on specified\nIP addresses or DNS entries to forward to a service using tproxy or tun interface,\ndepending on implementation.\n\nA proxy on the other hand will just start a regular TCP/UDP listener on the configured port,\nso traffic will have to be configured for that destination.\n\nExample proxy.v1 Configuration:\n\n```\n  {\n    \"port\": 8080,\n    \"protocols\": [\"tcp\"],\n    \"binding\": \"0.0.0.0\"\n  }\n```\n\nConfiguration Properties:\n  - port (required): Port number to listen on (1-65535)\n  - protocols (required): Array of supported protocols (tcp, udp)\n  - binding (optional): Interface to bind to. For the ER/T defaults to the configured lanIF config property.\n\nThis config type is currently supported by the ER/T when running in either proxy or tproxy mode.\n\n## Alert Events (BETA)\n\n*Originally released in 1.7.0*\n\nA new alert event type has been added to allow Ziti components to emit alerts for issues that network operators can address.\nAlert events are generated when components encounter problems such as service configuration errors or resource\navailability issues.\n\nAlert events include:\n  - Alert source type and ID (currently supports routers, with controller and SDK support planned for future releases)\n  - Severity level (currently supports error, with info and warning planned for future releases)\n  - Alert message and supporting details\n  - Related entities (router, identity, service, etc.) associated with the alert\n\nExample alert event when a router cannot bind a configured network interface:\n\n```\n  {\n    \"namespace\": \"alert\",\n    \"event_src_id\": \"ctrl1\",\n    \"timestamp\": \"2021-11-08T14:45:45.785561479-05:00\",\n    \"alert_source_type\": \"router\",\n    \"alert_source_id\": \"DJFljCCoLs\",\n    \"severity\": \"error\",\n    \"message\": \"error starting proxy listener for service 'test'\",\n    \"details\": [\n      \"unable to bind eth0, no address\"\n    ],\n    \"related_entities\": {\n      \"router\": \"DJFljCCoLs\",\n      \"identity\": \"DJFljCCoLs\",\n      \"service\": \"3DPjxybDvXlo878CB0X2Zs\"\n    }\n  }\n```\n\nAlert events can be consumed through the standard event system and logged to configured event handlers for monitoring and alerting purposes.\n\nThese events are currently in Beta, as the format is still subject to change. Once they've been in use in production for a while\nand proven useful, they will be marked as stable.\n\n## Azure Service Bus Event Sink\n\n*Originally released in 1.7.0. Contributed by @ffaraone.*\n\nAdds support for streaming controller events to Azure Service Bus.\nThe new logger enables real-time event streaming from the OpenZiti controller to Azure Service Bus\nqueues or topics, providing integration with Azure-based monitoring and analytics systems.\n\nTo enable the Azure Service Bus event logger, add configuration to the controller config file under the events section:\n\n```\n  events:\n    serviceBusLogger:\n      subscriptions:\n        - type: circuit\n        - type: session\n        - type: metrics\n          sourceFilter: .*\n          metricFilter: .*\n        # Add other event types as needed\n      handler:\n        type: servicebus\n        format: json\n        connectionString: \"Endpoint=sb://your-namespace.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=your-key\"\n        topic: \"ziti-events\"          # Use 'topic' for Service Bus topic\n        # queue: \"ziti-events-queue\"  # Or use 'queue' for Service Bus queue\n        bufferSize: 100                # Optional, defaults to 50\n```\n\n- Required configuration:\n    - format: Event format, currently supports only json\n    - connectionString: Azure Service Bus connection string\n    - Either topic or queue: Destination name (mutually exclusive)\n\n- Optional configuration:\n    - bufferSize: Internal message buffer size (default: 50)\n\n## OIDC is now enabled by default\n\nThe controller now automatically adds the `edge-oidc` API binding to any web listener that hosts\nthe `edge-client` API, even when `edge-oidc` is not explicitly listed in the `web` section of the\nconfiguration. This means OIDC-based authentication is available out of the box without requiring\nchanges to existing controller configurations.\n\n### Where OIDC binds\n\nThe `edge-oidc` binding is added to the same web listener(s) as `edge-client`. If `edge-client` is\nhosted on `127.0.0.1:1280`, the OIDC endpoints will be available on that same address under the\n`/oidc` path (e.g. `https://127.0.0.1:1280/oidc/.well-known/openid-configuration`).\n\nThe controller capabilities returned by `GET /version` will include `OIDC_AUTH` and the\n`edge-oidc` entry will be present in `apiVersions` when OIDC is active.\n\n### Opting out\n\nIf OIDC should not be enabled automatically, set `disableOidcAutoBinding: true` under `edge.api`:\n\n```yaml\nedge:\n  api:\n    address: 127.0.0.1:1280\n    sessionTimeout: 30m\n    disableOidcAutoBinding: true\n```\n\nWhen `disableOidcAutoBinding` is `true`, OIDC will only be active if `edge-oidc` is explicitly\nlisted as a binding in the `web` section. \n\nWhen OIDC is not enabled, all (SDK) clients will revert to using legacy authentication.\nLegacy authentication does not support multiple controllers or HA in general. Clients will treat\ntheir controller as if it is a single controller instance and will function as if no other controllers\nexist.\n\n\n## WWW-Authenticate Headers\n\nThe controller's Edge APIs now include `WWW-Authenticate` headers on `401 Unauthorized` responses,\nper issuer: https://github.com/openziti/ziti/issues/3356. These headers provide insight into why\na token was rejected. The main benefit of these headers is to convey information for JWT backed\nAPI Sessions and API Session authentication where a token may not have been provided (missing),\nis used beyond its expiration date (expired), or the token has become invalid for any other\nreason (invalid).\n\n`www-authenticate` headers are provided as a single header instance with multiple challenge values\nseparate by commas.\n\n### No Credentials Provided\n\nWhen a request hits a protected endpoint without any credentials, a single `WWW-Authenticate` header\nis returned listing both accepted auth schemes as comma-separated challenges:\n\n```\nWWW-Authenticate: zt-session realm=\"zt-session\" error=\"missing\" error_description=\"no matching token was provided\",Bearer realm=\"openziti-oidc\" error=\"missing\" error_description=\"no matching token was provided\"\n```\n\n### Token Errors\n\nWhen a token is present but cannot be accepted, the header identifies the scheme and what went wrong:\n\n```\nWWW-Authenticate: Bearer realm=\"openziti-oidc\" error=\"expired\" error_description=\"token expired\"\nWWW-Authenticate: Bearer realm=\"openziti-oidc\" error=\"invalid\" error_description=\"token is invalid\"\nWWW-Authenticate: zt-session realm=\"zt-session\" error=\"invalid\" error_description=\"token is invalid\"\n```\n\n### OIDC External JWT — Primary Authentication\n\nWhen an auth policy requires an external JWT signer for primary authentication (e.g., a PKCE flow\nbacked by an ext-jwt signer), the header identifies which signers are accepted and what went wrong.\nMultiple accepted signers are pipe-delimited in the `id` and `issuer` parameters:\n\n```\nWWW-Authenticate: Bearer realm=\"openziti-primary-ext-jwt\" error=\"missing\" error_description=\"no matching token was provided\" id=\"signer-id-1|signer-id-2\" issuer=\"https://issuer1.example.com|https://issuer2.example.com\"\n```\n\nThe `error` value follows the same `missing`/`expired`/`invalid` pattern as standard bearer token errors.\n\n### OIDC External JWT — Secondary / MFA Authentication\n\nWhen an auth policy requires an external JWT signer as a secondary factor (step-up after primary\nauth succeeds), the header identifies the single required signer. The `error` value follows the\nsame `missing`/`expired`/`invalid` pattern:\n\n```\nWWW-Authenticate: Bearer realm=\"openziti-secondary-ext-jwt\" error=\"missing\" error_description=\"no matching token was provided\" id=\"<signer-id>\" issuer=\"<issuer>\"\n```\n\n### Anonymous Endpoints\n\nUnauthenticated endpoints such as version information do not return `WWW-Authenticate` headers.\n\n## HA Preferred Leaders\n\nControllers can be marked as a preferred leader. \n\n**Example Config**\n```yaml\ncluster:\n  dataDir: /home/{{ .Model.MustVariable \"credentials.ssh.username\" }}/fablab/ctrldata\n  preferredLeader: true\n```\n\nIf a controller that is not marked preferredLeader becomes a preferredLeader, it will check \nif there's a node available that is marked as preferred. If there is one, or one later\njoins the cluster, the non-preferred node will attempt to transfer leadership to the \nnode that is marked as preferred.\n\n## Expanded Dynamic Cost Range\n\nThe dynamic cost range for smart routing has been expanded beyond the previous 64K limit. Under high\nload, terminators could saturate the cost space, making dynamic cost values meaningless for routing\ndecisions and leading to uneven load distribution. The expanded range allows for more granular cost\ndifferentiation even under heavy load.\n\n## Circuit ID and Error in Dial Failures\n\nDial failures now return the circuit ID and, when available, the error that caused the circuit to fail.\nPreviously, the circuit ID was only returned on successful dials. Note that SDKs will need to be\nupdated to surface the circuit id when a dial failure happens.\n\n## Multi-Underlay Control Channels\n\nRouter-to-controller control channels now support multiple underlays with priority-based message routing.\nThis allows time-sensitive control messages (heartbeats, routing, circuit requests) to be separated from\noperational data (metrics, inspections) across dedicated TCP connections, preventing bulk operations from\ndelaying user-affecting control plane traffic.\n\n## Dialing Identity Forwarded to Hosting SDK\n\nThe identity ID and name of the dialing client are now forwarded to the hosting SDK when a circuit is\nestablished. This allows hosting applications to identify which identity initiated the connection,\nenabling identity-aware request handling on the server side. This will require SDK updates to add this\nto the API for hosting applications.\n\n## Controller-Initiated Control Channel Dials (BETA)\n\nControllers can now dial routers to establish control channels. Previously, routers were solely\nresponsible for dialing controllers. This feature is designed for deployments where one or more\ncontrollers are in a private network that routers cannot reach, but the controllers can dial out.\nA common scenario is an HA cluster where some controllers are publicly reachable and some are in\na private network. External routers connect to the public controllers normally, and the private\ncontrollers dial out to the routers.\n\n### Router Configuration\n\nRouters can configure one or more control channel listeners. Each listener specifies a bind address,\nan advertise address (reported to the controller), and optional groups for matching.\n\n```yaml\nctrl:\n  listeners:\n    - bind: tls://0.0.0.0:6262\n      advertise: tls://router.example.com:6262\n      groups:\n        - default\n```\n\nThe router is the authoritative source of ctrl channel listener information, similar to link\nlisteners. When a router connects to any controller, it reports its configured `ctrlChanListeners`\nand the controller data model is updated automatically. This means that in most deployments —\nwhere the router can reach at least one controller — no manual configuration of listener addresses\nis needed on the controller side.\n\nThe `ctrlChanListeners` field can also be set via the CLI for cases where a router cannot reach\nany controller and thus cannot initialize the data model itself:\n\n```bash\nziti edge update edge-router myRouter --bootstrap-ctrl-chan-listener 'tls://router.example.com:6262=group1,group2'\n```\n\nGroups default to `[\"default\"]` if not specified.\n\n### Controller Configuration\n\nThe controller dialer is disabled by default and must be explicitly enabled. When enabled, the\ncontroller will dial routers that have control channel listeners configured and are not already\nconnected.\n\n```yaml\nctrl:\n  dialer:\n    enabled: true\n    groups:\n      - default\n    dialDelay: 30s\n    minRetryInterval: 1s\n    maxRetryInterval: 5m\n    retryBackoffFactor: 1.5\n    fastFailureWindow: 5s\n    queueSize: 32\n    maxWorkers: 10\n```\n\n- `enabled` - Enables the controller dialer (default: `false`)\n- `groups` - List of groups to match against router listener groups (default: `[\"default\"]`)\n- `dialDelay` - Delay before the controller starts dialing after boot (default: `30s`)\n- `minRetryInterval` - Minimum backoff delay between dial retries (default: `1s`)\n- `maxRetryInterval` - Maximum backoff delay between dial retries (default: `5m`)\n- `retryBackoffFactor` - Multiplier applied to the retry delay on each failure, jittered +/- 0.5 (default: `1.5`)\n- `fastFailureWindow` - If a connection drops within this window after connecting, backoff continues rather than resetting (default: `5s`)\n- `queueSize` - Maximum number of pending dial jobs in the worker pool queue (default: `32`)\n- `maxWorkers` - Maximum number of concurrent dial workers (default: `10`)\n\nThe controller will only dial routers whose listener groups overlap with the controller's configured\ngroups. When a router advertises multiple ctrl channel listener addresses, the dialer rotates through\nthem on each failure so that an unreachable address does not block attempts to the others.\n\n### Metrics\n\nThe controller dialer worker pool exposes the following metrics under the `ctrl_channel.dialer` prefix:\n\n- `ctrl_channel.dialer.queue_size` - Current number of pending dial jobs in the queue\n- `ctrl_channel.dialer.worker_count` - Current number of dial worker goroutines\n- `ctrl_channel.dialer.busy_workers` - Number of workers currently executing a dial\n- `ctrl_channel.dialer.work_timer` - Timer tracking the duration of each dial attempt\n\n## SDK Inspection Support\n\nNew CLI commands have been added for inspecting SDK state, useful for diagnosing terminator and\nconnectivity issues.\n\n**Note:** SDK inspection requires SDK support. Currently only the Go SDK supports inspection,\nas of version 1.5.0. Other SDKs will need to add support before these commands can be used\nwith them.\n\n### `ziti fabric inspect sdk`\n\nRetrieves SDK context inspection data from identities connected to routers.\n\n```\nziti fabric inspect sdk <target-selector> <identity-id>\n```\n\nThe `<target-selector>` is a regex matching router IDs (use `.*` for all routers). The\n`<identity-id>` is the identity whose SDK context you want to inspect. The command returns\ndetailed state from the connected SDK instance, including active services, terminators, and\nconnection status.\n\n### `ziti agent tunnel dump-sdk`\n\nDumps SDK context information from a running `ziti tunnel` process via the IPC agent.\n\n```\nziti agent tunnel dump-sdk\n```\n\nReturns JSON-formatted inspection data for all SDK contexts registered in the tunnel process,\nincluding service listeners, connections, and terminator state.\n\n## Current Beta Features\n\n* Basic Permission System\n* Alert Events\n* Controller-Initiated Control Channel Dials\n\n## Component Updates and Bug Fixes\n\n* github.com/openziti/agent: [v1.0.31 -> v1.0.33](https://github.com/openziti/agent/compare/v1.0.31...v1.0.33)\n* github.com/openziti/channel/v4: [v4.2.28 -> v4.3.9](https://github.com/openziti/channel/compare/v4.2.28...v4.3.9)\n    * [Issue #235](https://github.com/openziti/channel/issues/235) - Bump allowed hello message headers size to 16k from 4k\n    * [Issue #228](https://github.com/openziti/channel/issues/228) - Ensure that Underlay never return nil on MultiChannel\n    * [Issue #226](https://github.com/openziti/channel/issues/226) - Allow specifying a minimum number of underlays for a channel, regardless of underlay type\n    * [Issue #225](https://github.com/openziti/channel/issues/225) - Add ChannelCreated to the UnderlayHandler API to allow handlers to be initialized with the channel before binding\n    * [Issue #224](https://github.com/openziti/channel/issues/224) - Update the underlay dispatcher to allow unknown underlay types to fall through to the default\n    * [Issue #222](https://github.com/openziti/channel/issues/222) - Allow injecting the underlay type into messages\n\n* github.com/openziti/edge-api: [v0.26.47 -> v0.27.5](https://github.com/openziti/edge-api/compare/v0.26.47...v0.27.5)\n    * [Issue #175](https://github.com/openziti/edge-api/issues/175) - ctrlChanListeners should have x-omit-empty: false attribute\n    * [Issue #170](https://github.com/openziti/edge-api/issues/170) - Add preferredLeader flag to controllers\n    * [Issue #167](https://github.com/openziti/edge-api/issues/167) - Add ctrlChanListeners to router types\n    * [Issue #164](https://github.com/openziti/edge-api/issues/164) - Add permissions list to identity\n\n* github.com/openziti/foundation/v2: [v2.0.72 -> v2.0.89](https://github.com/openziti/foundation/compare/v2.0.72...v2.0.89)\n    * [Issue #472](https://github.com/openziti/foundation/issues/472) - Add support for multi-bit set/get to AtomicBitSet\n    * [Issue #464](https://github.com/openziti/foundation/issues/464) - Add support for -pre in versions\n    * [Issue #455](https://github.com/openziti/foundation/issues/455) - Correctly close goroutine pool when external close is signaled\n    * [Issue #452](https://github.com/openziti/foundation/issues/452) - Goroutine pool with a min worker count of 1 can drop to 0 workers due to race condition\n\n* github.com/openziti/identity: [v1.0.111 -> v1.0.127](https://github.com/openziti/identity/compare/v1.0.111...v1.0.127)\n    * [Issue #68](https://github.com/openziti/identity/issues/68) - Shutdown file watcher when stopping identity watcher\n\n* github.com/openziti/metrics: [v1.4.2 -> v1.4.3](https://github.com/openziti/metrics/compare/v1.4.2...v1.4.3)\n    * [Issue #56](https://github.com/openziti/metrics/issues/56) - underlying resources of reference counted meters are not cleaned up when reference count hits zero\n\n* github.com/openziti/runzmd: [v1.0.80 -> v1.0.90](https://github.com/openziti/runzmd/compare/v1.0.80...v1.0.90)\n* github.com/openziti/sdk-golang: [v1.2.3 -> v1.5.3](https://github.com/openziti/sdk-golang/compare/v1.2.3...v1.5.3)\n    * [Issue #887](https://github.com/openziti/sdk-golang/issues/887) - Fix listener manager cleanup\n    * [Issue #886](https://github.com/openziti/sdk-golang/issues/886) - When controller is busy during service refresh, backoff and retry instead of falling back to full refresh\n    * [Issue #885](https://github.com/openziti/sdk-golang/issues/885) - Only compare relevant service fields when looking for changes\n    * [Issue #884](https://github.com/openziti/sdk-golang/issues/884) - Add deadline for bind establishment\n    * [Issue #883](https://github.com/openziti/sdk-golang/issues/883) - Router level listener can be left open if multi-listener closes during listener establishment\n    * [Issue #877](https://github.com/openziti/sdk-golang/issues/877) - Handle differences in xgress eof/end-of-circuit handling by adding a capabilities exchange\n    * [Issue #832](https://github.com/openziti/sdk-golang/issues/832) - Fuzz session refresh timers\n    * [Issue #879](https://github.com/openziti/sdk-golang/issues/879) - Return the connId in inspect response\n    * [Issue #878](https://github.com/openziti/sdk-golang/issues/878) - Fix responses from rx goroutines\n    * [Issue #874](https://github.com/openziti/sdk-golang/issues/874) - Add inspect support at the context level\n    * [Issue #871](https://github.com/openziti/sdk-golang/issues/871) - Make SDK better at sticking to MaxTerminator terminators\n    * [Issue #708](https://github.com/openziti/sdk-golang/issues/708) - Support for Go's built-in context in Dial methods\n    * [Issue #860](https://github.com/openziti/sdk-golang/issues/860) - Make the dialing identity's id and name available on dialed connections\n    * [Issue #857](https://github.com/openziti/sdk-golang/issues/857) - Use new error code and retry hints to correctly react to terminator errors\n    * [Issue #847](https://github.com/openziti/sdk-golang/issues/847) - Ensure the initial version check succeeds, to ensure we don't legacy sessions on ha or oidc-enabled controllers\n    * [Issue #824](https://github.com/openziti/sdk-golang/pull/824) - release notes and hard errors on no TOTP handler breaks partial auth events\n    * [Issue #818](https://github.com/openziti/sdk-golang/issues/818) - Full re-auth should not clear services list, as that breaks the on-change logic\n    * [Issue #817](https://github.com/openziti/sdk-golang/issues/817) - goroutines can get stuck when iterating over randomized HA controller list\n    * [Issue #736](https://github.com/openziti/sdk-golang/issues/736) - Migrate from github.com/mailru/easyjson\n    * [Issue #813](https://github.com/openziti/sdk-golang/issues/813) - SDK doesn't stop close listener when it detects that a service being hosted gets deleted\n    * [Issue #811](https://github.com/openziti/sdk-golang/issues/811) - Credentials are lost when explicitly set\n    * [Issue #807](https://github.com/openziti/sdk-golang/issues/807) - Don't send close from rxer to avoid blocking\n    * [Issue #800](https://github.com/openziti/sdk-golang/issues/800) - Tidy create service session logging\n\n* github.com/openziti/secretstream: [v0.1.39 -> v0.1.48](https://github.com/openziti/secretstream/compare/v0.1.39...v0.1.48)\n* github.com/openziti/storage: [v0.4.26 -> v0.4.39](https://github.com/openziti/storage/compare/v0.4.26...v0.4.39)\n    * [Issue #122](https://github.com/openziti/storage/issues/122) - StringFuncNode has incorrect nil check, allowing panic\n    * [Issue #120](https://github.com/openziti/storage/issues/120) - Change post tx commit constraint handling order\n    * [Issue #119](https://github.com/openziti/storage/issues/119) - Add ContextDecorator API\n\n* github.com/openziti/transport/v2: [v2.0.188 -> v2.0.214](https://github.com/openziti/transport/compare/v2.0.188...v2.0.214)\n    * [Issue #31](https://github.com/openziti/transport/issues/31) - ipv6 Transport Address Parsing\n    * [Issue #149](https://github.com/openziti/transport/issues/149) - Archive transwarp code\n\n* github.com/openziti/xweb/v3: [v2.3.4 -> v3.0.4](https://github.com/openziti/xweb/compare/v2.3.4...v3.0.4)\n    * [Issue #32](https://github.com/openziti/xweb/issues/32) - watched identities sometimes don't reload when changed\n\n* github.com/openziti/go-term-markdown: v1.0.1 (new)\n* github.com/openziti/ziti/v2: [v1.6.8 -> v2.0.0](https://github.com/openziti/ziti/compare/v1.6.8...v2.0.0)\n    * [Issue #3648](https://github.com/openziti/ziti/issues/3648) - tunnel: myCopy logs router ID as circuitId, causing misleading debug output\n    * [Issue #3658](https://github.com/openziti/ziti/issues/3658) - Raft cluster join fails with \"hello message too big\" when using long hostnames\n    * [Issue #3626](https://github.com/openziti/ziti/issues/3626) - controller: overlay bind point produces malformed URL in /versions apiBaseUrls\n    * [Issue #3635](https://github.com/openziti/ziti/issues/3635) - Allow controllers to dial routers to support more topologies\n    * [Issue #3607](https://github.com/openziti/ziti/issues/3607) - linux installer not upgradable from v1\n    * [Issue #3650](https://github.com/openziti/ziti/issues/3650) - Reroute doesn't proactively clean up orphaned route entries\n    * [Issue #3571](https://github.com/openziti/ziti/issues/3571) - Ensure 2.0 backwards compatibility with 1.6 and 1.5 using the smoketest\n    * [Issue #3636](https://github.com/openziti/ziti/issues/3636) - Adaptive rate limiter should use success rate rather than queue position\n    * [Issue #3600](https://github.com/openziti/ziti/issues/3600) - Add a preferredLeader flag, allow selected nodes to be preferred for raft leader, if they're available\n    * [Issue #3642](https://github.com/openziti/ziti/issues/3642) - Use xgress_common.Connection type for xgress_transport and xgress_proxy\n    * [Issue #3597](https://github.com/openziti/ziti/issues/3597) - Enable OIDC API by default\n    * [Issue #3624](https://github.com/openziti/ziti/issues/3624) - Multi-underlay control channel doesn't correctly handle lack of group secret on non-grouped underlays\n    * [Issue #3613](https://github.com/openziti/ziti/issues/3613) - Initializing cluster from existing db using `db:` config settings results in panic\n    * [Issue #3620](https://github.com/openziti/ziti/issues/3620) - Controller control channel heartbeats config parsing was erroneously nested under options parsing\n    * [Issue #3619](https://github.com/openziti/ziti/issues/3619) - Router connect events full sync interval was using min/max values meant for the batch interval\n    * [Issue #3618](https://github.com/openziti/ziti/issues/3618) - Router interfaceDiscovery.minReportInterval value was being set to checkInterval\n    * [Issue #3617](https://github.com/openziti/ziti/issues/3617) - Transit router disabled flag not passed through raft command structure\n    * [Issue #3333](https://github.com/openziti/ziti/issues/3333) - Updb user-lockout triggered by successful login attempts\n    * [Issue #3599](https://github.com/openziti/ziti/issues/3599) - Add gap detection and handling to router data model\n    * [Issue #3356](https://github.com/openziti/ziti/issues/3356) - Add WWW-Authenticate Headers\n    * [Issue #3074](https://github.com/openziti/ziti/issues/3074) - Dynamic cost range is too limited\n    * [Issue #3558](https://github.com/openziti/ziti/issues/3558) - terminator cost increased on egress dial success, not on circuit completion\n    * [Issue #3556](https://github.com/openziti/ziti/issues/3556) - global circuit costs not cleared when terminator is deleted\n    * [Issue #3557](https://github.com/openziti/ziti/issues/3557) - costing calculation for the weighted terminator selection strategy  is incorrect\n    * [Issue #2512](https://github.com/openziti/ziti/issues/2512) - Return circuit ID and error in dial failures\n    * [Issue #3569](https://github.com/openziti/ziti/issues/3569) - Version 2.0+ routers should not connect to controllers which do not support JWT formatted legacy sessions\n    * [Issue #3565](https://github.com/openziti/ziti/issues/3565) - Link dialer save 'is first conn' true, so all dials claim to be first, causing potential race condition\n    * [Issue #3550](https://github.com/openziti/ziti/issues/3550) - Support multi-underlay control channels\n    * [Issue #3535](https://github.com/openziti/ziti/issues/3535) - Remove the legacy xgress_edge_tunnel implementation\n    * [Issue #3547](https://github.com/openziti/ziti/issues/3547) - Add support for sending the dialing identity id and name to the hosting sdk\n    * [Issue #3541](https://github.com/openziti/ziti/issues/3541) - Remove option to disable the router data model in the controller\n    * [Issue #3540](https://github.com/openziti/ziti/issues/3540) - Handle UDP difference between proxy and tproxy implementations\n    * [Issue #3527](https://github.com/openziti/ziti/issues/3527) - ER/T UDP tunnels keep closed connections for 30s, preventing potential new good connections in that time\n    * [Issue #3526](https://github.com/openziti/ziti/issues/3526) - ER/T half-close logic is incorrect\n    * [Issue #3524](https://github.com/openziti/ziti/issues/3524) - Provide more error context to SDKs for terminator errors\n    * [Issue #3509](https://github.com/openziti/ziti/issues/3509) - Enforce policy on the router for oidc sessions, by closing open circuits and terminators when service access is lost\n    * [Issue #3531](https://github.com/openziti/ziti/issues/3531) - Remove created/updated/deleted terminator events. Obsoleted by entity change events.\n    * [Issue #3532](https://github.com/openziti/ziti/issues/3532) - Removed deprecated create identity <type> subcommands\n    * [Issue #3521](https://github.com/openziti/ziti/issues/3521) - Cleanup CLI to remove calls to os.Exit to be embed friendlier\n    * [Issue #3516](https://github.com/openziti/ziti/issues/3516) - Remove support for create terminator v1\n    * [Issue #3512](https://github.com/openziti/ziti/issues/3512) - Remove legacy link management code from the controller\n    * [Issue #3511](https://github.com/openziti/ziti/issues/3511) - router proxy mode fails to resolve interface if binding is 0.0.0.0\n    * [Issue #3503](https://github.com/openziti/ziti/issues/3503) - Allow routers to request current cluster membership information\n    * [Issue #3501](https://github.com/openziti/ziti/issues/3501) - Get cluster membership information from raft directly, rather than trying to cache it in the DB\n    * [Issue #3500](https://github.com/openziti/ziti/issues/3500) - Set a router data model timeline when initializing a new HA setup, rather than letting it stay blank\n    * [Issue #3504](https://github.com/openziti/ziti/issues/3504) - Reduce router data model full state updates\n    * [Issue #3492](https://github.com/openziti/ziti/pull/3492) - Bump openziti/ziti-console-assets from 3.12.9 to 4.0.0 in /dist/docker-images/ziti-controller in the all group\n    * [Issue #3484](https://github.com/openziti/ziti/issues/3484) - router ctrl channel handler for handling cluster changes has an initialization race condition\n    * [Issue #3477](https://github.com/openziti/ziti/issues/3477) - Optionally enable model changes triggered by login to be non-blocking and to be droppable if the system is under load\n    * [Issue #3473](https://github.com/openziti/ziti/issues/3473) - Enable tls handshake rate limiter by default and tweak default values.\n    * [Issue #3471](https://github.com/openziti/ziti/issues/3471) - Go tunneler is ignoring host config MaxConnections\n    * [Issue #3469](https://github.com/openziti/ziti/issues/3469) - Only send model updates on resubscribe if the RDM index has advanced\n    * [Issue #2573](https://github.com/openziti/ziti/issues/2573) - An edge router in a tight restart loop causes a resource leak on routers to which it connects.\n    * [Issue #3430](https://github.com/openziti/ziti/issues/3430) - Add permissions list to identity\n    * [Issue #2109](https://github.com/openziti/ziti/issues/2109) - Add Edge Management Read Only Capability\n    * [Issue #3435](https://github.com/openziti/ziti/issues/3435) - Add edge management API permissions by entity type and action\n    * [Issue #3441](https://github.com/openziti/ziti/issues/3441) - Update router connection tracker to interrogate active connections\n    * [Issue #3451](https://github.com/openziti/ziti/issues/3451) - ci - compare only stable releases when promoting\n    * [Issue #3437](https://github.com/openziti/ziti/issues/3437) - SDK OIDC token updates to routers should return an error if invalid\n    * [Issue #3348](https://github.com/openziti/ziti/issues/3348) - Unable to clear/reset the \"tags\" property on an entity to an empty object\n    * [Issue #3452](https://github.com/openziti/ziti/issues/3452) - `ziti agent cluster add` has bad behavior if the add address doesn't match the advertise address\n    * [Issue #3410](https://github.com/openziti/ziti/issues/3410) - Consolidate fabric REST API code with edge management and edge client code\n    * [Issue #3425](https://github.com/openziti/ziti/issues/3425) - RDM not properly responding to tunneler enabled flag changes\n    * [Issue #3420](https://github.com/openziti/ziti/issues/3420) - The terminator id cache uses the same id for all terminators in a host.v2 config, resulting in a single terminator\n    * [Issue #3419](https://github.com/openziti/ziti/issues/3419) - When using the router data model, precedence specified on the per-service identity mapping are incorrectly interpreted\n    * [Issue #3318](https://github.com/openziti/ziti/issues/3318) - Terminator creation seems to slow exponentially as the number of terminators rises from 10k to 20k to 40k\n    * [Issue #3407](https://github.com/openziti/ziti/issues/3407) - The CLI doesn't properly pass JWT authentication information to websocket endpoints\n    * [Issue #3359](https://github.com/openziti/ziti/issues/3359) - Ensure router data model subscriptions have reasonable performance and will scale\n    * [Issue #3354](https://github.com/openziti/ziti/issues/3354) - SDK/ENV Info from SDKs is not distributed in HA\n    * [Issue #3381](https://github.com/openziti/ziti/issues/3381) - the fabric service REST apis are missing the maxIdleTime property\n    * [Issue #3382](https://github.com/openziti/ziti/issues/3382) - Legacy service sessions generated pre-1.7.x are incompatible with v1.7.+ and need to be cleared\n    * [Issue #3339](https://github.com/openziti/ziti/issues/3339) - get router ctrl.endpoint from ctrls claim in JWT\n    * [Issue #3378](https://github.com/openziti/ziti/issues/3378) - login with file stopped working\n    * [Issue #3349](https://github.com/openziti/ziti/issues/3349) - UPDB OIDC login returns wrong content type\n    * [Issue #2324](https://github.com/openziti/ziti/issues/2324) - Add Ext-JWT/OIDC enrollment\n    * [Issue #3346](https://github.com/openziti/ziti/issues/3346) - Fix confusing attempt logging\n    * [Issue #3337](https://github.com/openziti/ziti/issues/3337) - Router reports \"no xgress edge forwarder for circuit\"\n    * [Issue #3345](https://github.com/openziti/ziti/issues/3345) - Clean up connect events tests and remove global XG registry\n    * [Issue #3264](https://github.com/openziti/ziti/issues/3264) - Allow routers to generate alert events in cases of service misconfiguration\n    * [Issue #3321](https://github.com/openziti/ziti/issues/3321) - Health Check API missing base path on discovery endpoint\n    * [Issue #3323](https://github.com/openziti/ziti/issues/3323) - router/tunnel static services fail to bind unless new param protocol is defined\n    * [Issue #3309](https://github.com/openziti/ziti/issues/3309) - Detect link connections meant for another router\n    * [Issue #3286](https://github.com/openziti/ziti/issues/3286) - edge-api binding doesn't have the correct path on discovery endpoints\n    * [Issue #3297](https://github.com/openziti/ziti/issues/3297) - stop promoting hotfixes downstream\n    * [Issue #3295](https://github.com/openziti/ziti/issues/3295) - make ziti tunnel service:port pairs optional\n    * [Issue #3291](https://github.com/openziti/ziti/issues/3291) - replace decommissioned bitnami/kubectl\n    * [Issue #3277](https://github.com/openziti/ziti/issues/3277) - Router can deadlock on closing a connection if the incoming data channel is full\n    * [Issue #3269](https://github.com/openziti/ziti/issues/3269) - Add host-interfaces config type\n    * [Issue #3258](https://github.com/openziti/ziti/issues/3258) - Add config type proxy.v1 so proxies can be defined dynamically for the ER/T\n    * [Issue #3259](https://github.com/openziti/ziti/issues/3259) - Interfaces config type not added due to wrong name\n    * [Issue #3265](https://github.com/openziti/ziti/issues/3265) - Forwarding errors should log at debug, since they are usual part of circuit teardown\n    * [Issue #3261](https://github.com/openziti/ziti/issues/3261) - ER/T dialed xgress connections may only half-close when peer is fully closed\n    * [Issue #3207](https://github.com/openziti/ziti/issues/3207) - Allow router embedders to customize config before start\n\n\n","mentions_count":1},{"url":"https://api.github.com/repos/openziti/ziti/releases/294008448","assets_url":"https://api.github.com/repos/openziti/ziti/releases/294008448/assets","upload_url":"https://uploads.github.com/repos/openziti/ziti/releases/294008448/assets{?name,label}","html_url":"https://github.com/openziti/ziti/releases/tag/v2.0.0-pre3","id":294008448,"author":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"node_id":"RE_kwDODVFMN84RhjaA","tag_name":"v2.0.0-pre3","target_commitish":"main","name":"v2.0.0-pre3","draft":false,"immutable":false,"prerelease":true,"created_at":"2026-03-06T16:11:56Z","updated_at":"2026-03-06T16:26:15Z","published_at":"2026-03-06T16:26:15Z","assets":[{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/368380646","id":368380646,"node_id":"RA_kwDODVFMN84V9Qrm","name":"checksums.sha256.txt","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"text/plain; charset=utf-8","state":"uploaded","size":790,"digest":"sha256:ed37758f41956f0fe980dac89059185125ace6b961c3d0ef0b32a92acd9661ea","download_count":3,"created_at":"2026-03-06T16:26:13Z","updated_at":"2026-03-06T16:26:14Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre3/checksums.sha256.txt"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/368380644","id":368380644,"node_id":"RA_kwDODVFMN84V9Qrk","name":"sbom-v2.0.0-pre3.spdx.json","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/json","state":"uploaded","size":1023179,"digest":"sha256:ccdcf22a4c522acf6447a47fd8187ca063329e12d8767a4702e31cc0e2e6789f","download_count":4,"created_at":"2026-03-06T16:26:13Z","updated_at":"2026-03-06T16:26:14Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre3/sbom-v2.0.0-pre3.spdx.json"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/368380647","id":368380647,"node_id":"RA_kwDODVFMN84V9Qrn","name":"source-v2.0.0-pre3.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":3712206,"digest":"sha256:57057a0c162c0bf9109c7bfee2e7b8f83bd9985fa68a01c38d49e8562666e54b","download_count":4,"created_at":"2026-03-06T16:26:13Z","updated_at":"2026-03-06T16:26:14Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre3/source-v2.0.0-pre3.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/368380605","id":368380605,"node_id":"RA_kwDODVFMN84V9Qq9","name":"ziti-darwin-amd64-2.0.0-pre3.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":53963272,"digest":"sha256:85a5135781ef006e122a71666ea678b839b02a4b4f84cb6b74e4ab305eefa215","download_count":6,"created_at":"2026-03-06T16:26:10Z","updated_at":"2026-03-06T16:26:13Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre3/ziti-darwin-amd64-2.0.0-pre3.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/368380602","id":368380602,"node_id":"RA_kwDODVFMN84V9Qq6","name":"ziti-darwin-arm64-2.0.0-pre3.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":50289226,"digest":"sha256:168e380a7f65f5e6e7204f729a3e5fe882cf0ec01344c5f9fec04c7bf5489b71","download_count":4,"created_at":"2026-03-06T16:26:10Z","updated_at":"2026-03-06T16:26:13Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre3/ziti-darwin-arm64-2.0.0-pre3.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/368380606","id":368380606,"node_id":"RA_kwDODVFMN84V9Qq-","name":"ziti-linux-amd64-2.0.0-pre3.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":55265656,"digest":"sha256:0ec94de48a485a977b5619d2d8fa26c189c75b0e45d65eb612950d2d68ee30c8","download_count":16,"created_at":"2026-03-06T16:26:10Z","updated_at":"2026-03-06T16:26:13Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre3/ziti-linux-amd64-2.0.0-pre3.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/368380604","id":368380604,"node_id":"RA_kwDODVFMN84V9Qq8","name":"ziti-linux-arm-2.0.0-pre3.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":51698516,"digest":"sha256:e3d4e0d248880d45a4ab0d512e7e4f8766691b183f1fac1932546a5d9db1e0e1","download_count":7,"created_at":"2026-03-06T16:26:10Z","updated_at":"2026-03-06T16:26:13Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre3/ziti-linux-arm-2.0.0-pre3.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/368380641","id":368380641,"node_id":"RA_kwDODVFMN84V9Qrh","name":"ziti-linux-arm64-2.0.0-pre3.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":51765173,"digest":"sha256:797d03ba603ebb4154a54efcf4f0db4ce4ad98365ed19b010c16ef8d1ee0b5cf","download_count":9,"created_at":"2026-03-06T16:26:13Z","updated_at":"2026-03-06T16:26:15Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre3/ziti-linux-arm64-2.0.0-pre3.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/368380603","id":368380603,"node_id":"RA_kwDODVFMN84V9Qq7","name":"ziti-windows-amd64-2.0.0-pre3.zip","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/zip","state":"uploaded","size":44670429,"digest":"sha256:88fcd84c6d469d1c3b44ddb471ffab1d376fd90b7fd2fef338690004337aa0e0","download_count":5,"created_at":"2026-03-06T16:26:10Z","updated_at":"2026-03-06T16:26:13Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre3/ziti-windows-amd64-2.0.0-pre3.zip"}],"tarball_url":"https://api.github.com/repos/openziti/ziti/tarball/v2.0.0-pre3","zipball_url":"https://api.github.com/repos/openziti/ziti/zipball/v2.0.0-pre3","body":"# Release 2.0.0\n\n## What's New\n\nThis is the next major version release of OpenZiti, following the 1.0 release in April 2024. \nOf particular note is that HA controllers are now considered ready for general use. \nThis release also introduces a new permissions model, OIDC/JWT token-based enrollment, \nclustering performance improvements, and a number of other features and fixes. Because \nsome of these changes are not backwards compatible with older routers, we're marking this \nas a major version bump.\n\n### HA Controllers are now considered ready for general use\n\nThis is a pretty big milestone and marks the completion of work that's been ongoing for a couple of years.\nThe HA work has brought with it some notable changes. Authentication now uses JWTs by default. Using JWTs\nmeans the controllers don't need to store session and propagate them to the routers. This removes a bottleneck\nfrom the network and allows the load to be more easily distributed among controllers and routers.\n\nTo support distributed authentication, the routers now get a bespoke version of the data model. In addition\nto enabling distributed authentication this will allow us to remove the need for service polling and further \nreduce the load on controllers in the future.\n\n### Router Compatibility\n\nRelated to the JWT work, routers with version 2.+ will only work with controllers that are version 2.+. This means\nto upgrade your network, controllers should be upgraded first. Routers can then be upgraded individually.\n\n2.x routers should still work fine with older router versions.\n\nWe try very hard to avoid breaking changes like this, but sometimes the engineering trade-offs lead there. This change\nwas first made in the 1.7 release. That release has not been marked stable, and we have no plans to do so, because\nof the backwards incompatibility. \n\nIf you need to support in the 1.6.12+ range, see the section \"OIDC enabled by default\".\n\n### New Permissions Model (BETA)\n\nAs one feature goes out of beta, another arrives into beta. This release introduces a new permissions system\nfor more fine grained control to the management API. It's not expected to change, but may do so based on feedback\nfrom users.\n\n### Updated Release Process\n\nWe have moved to creating `-preN` releases for major and minor versions. This way we can put out release candidates,\nor feature previews and put them through internal testing and let interested folks from the community try them out.\nThen, when we're ready, we can run the full validation suite against the last pre-release and retag it. \n\nPatch releases won't have `-preN` and should contain only high priority bug fixes.\n\n### Deprecation Cleanup\n\nSince we already have a breaking change, we're removing some other backwards compatibility code.\n\n* Controller managed links \n    * Router managed links were introduced in v0.30.0. \n    * If you're upgrading from an older versions, you'll want to upgrade to the latest 1.x release before jumping to 2.x\n    * Github tracking issue: https://github.com/openziti/ziti/issues/3512\n* `ziti edge create identity <type>`\n    * Identity types other than router were removed in v0.30.2\n    * The `type` can be dropped from the CLI command\n    * Github tracking issue: https://github.com/openziti/ziti/issues/3532\n* Terminator create/update/delete events\n    * These have been superseded by entity change events, which also have create/update/delete events for terminators\n    * Entity change events were introduced in v0.28.0\n    * Github tracking issue: https://github.com/openziti/ziti/issues/3531\n* `xgress_edge_tunnel` v1\n    * This is the first implementation of the tunneler in edge-router code (ER/T) which used legacy api sessions and services\n    * The v2 version uses the router data model and was introduced in v0.30.x\n    * Github tracking issue: https://github.com/openziti/ziti/issues/3516\n\n### Legacy Session Deprecation\n\nOIDC sessions are now preferred. They are the default, or will become the default for SDKs and tunnelers. They are also required\nwhen running HA. Legacy API and service session are now deprecated and will be removed in the OpenZiti v3.0.0 release. \n\n### Additional Features\n\n* Controllers can now optionally bind APIs using an OpenZiti identity\n* `ziti edge login` now supports the `--network-identity` flag to authenticate and establish connections through the Ziti overlay network\n* `ziti edge login` now supports using a bearer token with `--token` for authentication. The token is expected to be\n  provided as just the JWT, not with the \"Bearer \" prefix\n* Identity configuration can now be loaded from files or environment variables for flexible deployment scenarios\n* Identities can now be provisioned just-in-time through OIDC/JWT token-based enrollment\n* Multiple model updates can now be in-flight at the same time, improving clustering performance\n* Authentication-related model updates can now be non-blocking and even dropped if the system is too busy\n* Routers now provide more error context to SDKs for terminator errors, enabling better retry behavior\n* New `proxy.v1` config type for dynamic service proxies (originally released in 1.7.0)\n* New alert event type for surfacing operational issues to network operators - Beta (originally released in 1.7.0)\n* New Azure Service Bus event sink for streaming controller events, contributed by @ffaraone (originally released in 1.7.0)\n* Bundled ZAC upgraded to 4.0\n* Build updated to Go 1.25\n* CLI cleaned up to remove calls to `os.Exit`, making it more friendly for embedding\n* OIDC is now enabled by default\n* Controller Edge APIs now return `WWW-Authenticate` response headers on `401 Unauthorized` responses, giving clients actionable information about which auth methods are accepted and what went wrong\n* HA Controllers can be marked as 'preferredLeader' via config\n* Dynamic cost range for smart routing expanded beyond the previous 64K limit\n* Dial failures now return the circuit ID and error information for easier debugging\n* Router-to-controller control channels now support multiple underlays with priority-based message routing\n* The dialing identity's ID and name are now forwarded to the hosting SDK\n\n## Basic Permission System (BETA)\n\nAdded a basic permission system that allows more control over identity access to controller management API operations. \nThis replaces the previous binary admin/non-admin model with a more flexible permission system.\n\n**NOTE:** This feature is in BETA, primarily so we can get feedback on which permissions make sense. The implementation is unlikely to change\nbut the set of exposed permissions may grow, shrink or change based on user feedback. \n\n### Permission Model\n\nThe permission system supports three levels of authorization:\n\n  1. **Global Permissions**: System-wide access levels\n     - `admin` - Full access to all operations. This is still controlled by the `isAdmin` flag on identity\n     - `admin_readonly` - Read-only access to all resources except debugging facilities inspect and validate\n\n  2. **Entity-Level Permissions**: Full CRUD access to specific entity types\n     - Granting an entity-level permission (e.g., `service`) provides complete create, read, update, and delete access for that entity type\n\n  3. **Action-Level Permissions**: Specific operation access on entity types\n     - Fine-grained control using the pattern `<entity>.<action>` (e.g., `service.read`, `identity.update`)\n     - Supports `create`, `read`, `update`, and `delete` actions per entity type\n\n### Supported Entity Permissions\n\nThe following entity-level permissions are available:\n\n- `auth-policy` - Authentication policy management\n- `ca` - Certificate Authority management\n- `config` - Configuration management\n- `config-type` - Configuration type management\n- `edge-router-policy` - Edge router policy management\n- `enrollment` - Enrollment management\n- `external-jwt-signer` - External JWT signer management\n- `identity` - Identity management\n- `posture-check` - Posture check management\n- `router` - Edge and transit router management\n- `service` - Service management\n- `service-policy` - Service policy management\n- `service-edge-router-policy` - Service edge router policy management\n- `terminator` - Terminator management\n- `ops` - Operational resources (API sessions, sessions, circuits, links, inspect and validate)\n\n### Permission Assignment\n\nPermissions are assigned to identities via the `permissions` field in the identity resource. Multiple permissions can be granted to a single identity, and permissions are additive.\n\n### Cross-Entity Operations\n\nListing related entities through an entity's endpoints requires appropriate permissions for the related entity type. For example:\n- Listing services for a service-policy requires `service.read` permission\n- Listing identities for an edge-router-policy requires `identity.read` permission\n- Listing configs for a service requires `config.read` permission\n\n**NOTE:** \nMore permissions than expected may be required when performing actions through the CLI or ZAC. Take for example, when an identity \nhas `config.create` and is attempting to create a new config. The CLI may fail if the identity doesn't have `config-type.read`\nas well because it will need to look up the config type id that corresponds to the given config type name.\n\nSimilar cross entity read permissions may be required when creating services.\n\n### Admin Protection\n\nNon-admin identities cannot:\n- Create identities with the `isAdmin` flag\n- Create identities with any permissions granted\n- Modify admin-related fields on existing identities\n- Update or delete admin identities\n- Grant permissions to identities\n\nThese protections ensure that privilege escalation is prevented and admin access remains controlled.\n\n\n## Binding Controller APIs With Identity\n\nController APIs can now be bound to an OpenZiti overlay network identity, allowing secure communication through\nthe Ziti network. This is useful for scenarios where you want to expose controller APIs only through the overlay\nnetwork rather than on a standard network interface.\n\n### Configuration Structure\n\nA standard `bindPoint` configuration looks like this:\n```text\n    bindPoints:\n      - interface: 127.0.0.1:18441\n        address: 127.0.0.1:18441\n```\n\nTo bind controller APIs to an OpenZiti identity, add an additional `identity` block to your `bindPoints`. The\nidentity configuration specifies where to load the Ziti identity file and which service to bind it to:\n\n```text\n    bindPoints:\n      - interface: 127.0.0.1:18441\n        address: 127.0.0.1:18441\n      - identity:\n          file: \"c:/temp/ctrl.testing/ctrl.identity.json\"\n          service: \"mgmt\"\n```\n\n### Supported Configuration Options\n\n- `file`: Path to a Ziti identity JSON file containing the controller's identity and enrollment certificate\n- `env`: Name of an environment variable containing a base64-encoded Ziti identity (alternative to `file`)\n- `service`: The name of the Ziti service to bind the controller API to\n\n### Using Environment Variables\n\nFor deployments where storing identity files on disk is not preferred, you can reference a base64-encoded\nidentity file from an environment variable. The environment variable should contain the base64-encoded contents\nof the identity JSON file.\n\nFor example, if an environment variable named `ZITI_CTRL_IDENTITY` contains a base64-encoded identity file:\n\n```text\n    bindPoints:\n      - interface: 127.0.0.1:18441\n        address: 127.0.0.1:18441\n      - identity:\n          env: ZITI_CTRL_IDENTITY\n          service: \"mgmt\"\n```\n\n### IPv6 Support\n\nBoth IPv4 and IPv6 addresses are supported for standard bind points. IPv6 addresses should be specified in bracket\nnotation with a port number:\n\n```text\n    bindPoints:\n      - interface: \"[::1]:18441\"\n        address: \"[::1]:18441\"\n      - identity:\n          file: \"/path/to/identity.json\"\n          service: \"mgmt\"\n```\n\n## CLI Enhancements for Identity-Based Connections\n\nThe `ziti edge login` command and REST client utilities have been enhanced to support identity-based connections\nthrough the Ziti overlay network.\n\n### New `--network-identity` Flag for `ziti edge login`\n\nThe `ziti edge login` command now includes a `--network-identity` flag that allows you to authenticate to a Ziti\ncontroller through the overlay network using a Ziti identity:\n\n```bash\nziti edge login https://ziti.mgmt.apis.local:1280 \\\n  --username myuser \\\n  --password mypass \\\n  --network-identity /path/to/identity.json\n```\n\nThis is useful when the controller is only accessible through the Ziti overlay network or when you want to ensure\nall communication to the controller flows through the overlay for security purposes.\n\n### Identity Resolution Order\n\nWhen establishing connections, identities are resolved in the following order:\n\n1. **Command-line flag**: The `--network-identity` flag takes precedence\n2. **Environment variable**: If `ZITI_CLI_NETWORK_ID` is set and contains a base64-encoded identity, it is used\n3. **Cached identity file**: If a network identity was saved from a previous login in the Ziti config directory, it may be used\n\nThis layered approach allows for flexibility in deployment scenarios:\n- Development: Use command-line flags for quick testing\n- Automation: Use environment variables in CI/CD pipelines\n- Production: Cache identities securely for repeated access\n\n#### Dialing Modes When Authenticating\n\nThe CLI supports two dialing modes:\n\n**Intercept-based Dialing (Default)**\nBy default, URLs are expected to leverage intercepts. Create a service with an appropriate intercept config and use\nthe intercept address when dialing. This is the standard mode for most use cases. For example, given a service with\nthe intercept `ziti.mgmt.apis.local`\n```bash\nziti edge login https://ziti.mgmt.apis.local:1280 \\\n  --username myuser \\\n  --password mypass \\\n  --network-identity /path/to/identity.json\n```\n\n**Identity-aware Dialing (Addressable Terminators)**\nTo support addressable terminators-based dialing, specify a user in the URL. This activates dial-by-identity\nfunctionality. The URL format should be `identity-to-dial@service-name-to-dial`. For example:\n```bash\nziti edge login https://my-identity@my-service:1280 \\\n  --username myuser \\\n  --password mypass \\\n  --network-identity /path/to/identity.json\n```\n\nIn this mode, the transport extracts the identity from the URL and uses it to establish a direct connection to\nthe specified service via the addressable terminator.\n\n\n## OIDC/JWT Token-based Enrollment\n\nOpenZiti now supports provisioning identities just-in-time through OIDC/JWT token enrollment. External identity \nproviders can be configured to allow identities to enroll using JWT tokens, with support for the resulting \nidentities to use certificate or token authentication.\n\n### External JWT Signer Configuration\n\nExternal JWT signers are configured via the Edge Management API to define enrollment behavior with the following new \nenrollment-specific properties:\n\n- **enrollToCertEnabled** - When enabled, identities can exchange a JWT token and a certificate signing request (CSR) \n        for a client certificate during enrollment. The certificate can then be used for standard certificate-based\n        authentication.\n\n- **enrollToTokenEnabled** - When enabled, identities can use a JWT token to enroll. The current token or future tokens\n        may be used for authentication.\n\n- **enrollNameClaimsSelector** - Specifies which JWT claim contains the identity name. Accepts a JSON pointer \n        (e.g., `/preferred_username`) or a simple property name (e.g., `preferred_username`, automatically converted to\n        `/preferred_username`). Defaults to `/sub` if not specified. The extracted value becomes the identity name in Ziti.\n\n- **enrollAttributeClaimsSelector** - Specifies which JWT claims to extract as identity attributes during enrollment.\n        Accepts a JSON pointer (e.g., `/roles`) or a simple property name (e.g., `roles`). Extracted attributes are \n        applied to the newly enrolled identity for use in authorization policies.\n\n- **enrollAuthPolicyId** - Specifies the authentication policy to apply to newly enrolled identities. This determines\n        what authentication methods are available for the identity post-enrollment.\n\nAdditionally the existing property named **claimsProperty** that specifies external id to match identities to:\n\n- now supports a JSON pointer (e.g., `/id`) or a simple property name (e.g., `id`)\n- is used to populate the `externalId` field of the identity\n\n### Enrollment Paths\n\n#### Certificate Enrollment (enrollToCertEnabled)\n\nWhen certificate enrollment is enabled, unauthenticated users can:\n\n1. Obtain a list of available IdPs from the public Edge Client API `GET /external-jwt-signers` endpoint, where \n   `enrollToCertEnabled` is set to `true`\n2. Obtain a JWT from the configured OIDC provider\n3. Generate a certificate signing request (CSR)\n4. Submit an enrollment request with the JWT and CSR\n5. Have their identity created in Ziti with attributes extracted from JWT claims\n6. Receive a signed client certificate for certificate-based authentication\n\n#### Token Enrollment (enrollToTokenEnabled)\n\nWhen token enrollment is enabled, unauthenticated users can:\n\n1. Obtain a list of available IdPs from the public Edge Client API `GET /external-jwt-signers` endpoint, where \n   `enrollToTokenEnabled` is set to `true`\n1. Obtain a JWT from the configured OIDC provider\n2. Submit an enrollment request with the JWT\n3. Have their identity created in Ziti with attributes extracted from JWT claims\n4. Receive a Ziti API token for token-based authentication\n\n### Edge Management API\n\nThe Edge Management API provides full CRUD operations for configuring external JWT signers:\n\n- `POST /external-jwt-signers` - Create a new external JWT signer with all configuration options\n- `GET /external-jwt-signers` - List all configured external JWT signers\n- `GET /external-jwt-signers/{id}` - Retrieve a specific signer configuration\n- `PUT /external-jwt-signers/{id}` - Update all fields of a signer\n- `PATCH /external-jwt-signers/{id}` - Partially update a signer\n- `DELETE /external-jwt-signers/{id}` - Delete a signer\n\n### Edge Client API\n\nThe Edge Client API exposes a reduced set of external JWT signer information for unauthenticated enrollment requests:\n\n- `GET /external-jwt-signers` - List available JWT signers with enrollment capabilities\n\nThe client API response includes the following fields for each signer:\n\n- `name` - Signer name\n- `externalAuthUrl` - URL where users obtain JWT tokens\n- `clientId` - OIDC client ID\n- `scopes` - Requested OIDC scopes\n- `openIdConfigurationUrl` - OIDC discovery endpoint\n- `audience` - Expected token audience\n- `targetToken` - Token type to use (ACCESS or ID)\n- **`enrollToCertEnabled`** - Flag indicating certificate enrollment is available\n- **`enrollToTokenEnabled`** - Flag indicating token enrollment is available\n\n### CLI Commands\n\n**Create an external JWT signer with enrollment options:**\n```\nziti edge controller create ext-jwt-signer <name> <issuer> \\\n  --jwks-endpoint <url> \\\n  --audience <audience> \\\n  --enroll-to-cert \\\n  --enroll-to-token=false \\\n  --enroll-name-claims-selector preferred_username \\\n  --enroll-attr-claims-selector roles \\\n  --enroll-auth-policy <policy-id-or-name>\n```\n\n**Update enrollment options on an existing signer:**\n```\nziti edge controller update ext-jwt-signer <name|id> \\\n  --enroll-to-cert \\\n  --enroll-auth-policy <policy-id-or-name>\n```\n\n**List external JWT signers:**\n```\nziti edge controller list ext-jwt-signers\n```\n\n## Clustering Performance Improvements\n\nIn previous releases, model updates were submitted to raft one at at time. This prevented \nraft from being efficient by allowing command batching. This release allows multiple \nmodel updates to be in-flight at the same time. \n\nNew Configuration Options\n\n1. Raft Apply Timeout (cluster.applyTimeout)\n\nLocation: Controller configuration file, under the cluster section\nType: Duration\nDefault: 5s\nDescription: Timeout for applying commands to the Raft distributed log. Commands that exceed this timeout will trigger adaptive rate limiter backoff.\n\nExample:\n```\ncluster:\n  applyTimeout: 10s\n```\n\n2. Command Rate Limiter Configuration (command.rateLimiter)\n\nA new adaptive rate limiter that controls the submission of commands to the Raft cluster. Unlike the existing command rate limiter, this specifically manages in-flight Raft operations with adaptive window sizing.\n\nConfiguration Structure:\n```\ncommand:\n  rateLimiter:\n    enabled: true\n    minSize: 5\n    maxSize: 250\n    timeout: 30s\n```\n\nSub-options:\n\n  - enabled (boolean)\n    - Default: true\n    - Description: Enable/disable adaptive rate limiting for Raft command submission\n  - minSize (integer)\n    - Default: 5\n    - Minimum: 1\n    - Description: Minimum window size for concurrent in-flight Raft operations\n  - maxSize (integer)\n    - Default: 250\n    - Description: Maximum window size for concurrent in-flight Raft operations. Must be >= minSize\n  - timeout (duration)\n    - Default: 30s\n    - Description: Time after which outstanding work is assumed to have failed if not marked completed\n\n3. Restart Self on Snapshot (cluster.restartSelfOnSnapshot)\n\nLocation: Controller configuration file, under cluster section\nType: Boolean\nDefault: false\nDescription: When true, the controller will automatically restart itself when restoring a snapshot to an initialized system. When false, the controller will exit with code 0, requiring external process management to restart it.\n\nExample:\n```\ncluster:\n    restartSelfOnSnapshot: true\n```\n\n### New Metrics\n\nThe adaptive rate limiter exposes three new metrics:\n\n  1. raft.rate_limiter.queue_size (gauge)\n    - Current number of operations queued/in-flight\n  2. raft.rate_limiter.work_timer (timer)\n    - Duration of rate-limited operations\n  3. raft.rate_limiter.window_size (gauge)\n    - Current adaptive window size\n\n## Rate Limiter Algorithm Improvements\n\nThe adaptive rate limiter tracker now uses a success rate metric to decide when to grow or shrink its\nconcurrency window, instead of using raw queue position. An exponentially decaying histogram tracks the\nratio of successes to backoffs. When the success rate exceeds a configurable threshold, the window is\ngrown by a multiplicative increase factor. When it falls below the threshold, the window is shrunk by a\nmultiplicative decrease factor. The check intervals for increase and decrease are independently configurable.\n\nThis approach produces smoother, more stable window adjustments under varying load, avoiding the\nover-aggressive shrinking that could occur when queue position alone was used as the signal.\n\nThis specific rate limiter implementation is used in three places:\n* **Controller TLS handshake rate limiting** - controls the rate of incoming TLS handshakes on the controller\n* **Raft command submission** - controls the rate of commands submitted to the Raft distributed log\n* **Router control channel rate limiting** - controls the rate of requests from the router to the controller\n\n## Background Processing for Identity Updates\n\nIdentity environment and authenticator updates that occur during authentication are now processed asynchronously in the background. \nThis prevents authentication requests from blocking when the system is under load, significantly improving resilience during thundering herd scenarios.\n\nWhen the background queue fills up, updates can be dropped as they will be refreshed on the next authentication attempt. \nThis allows the system to gracefully handle load spikes without impacting authentication performance.\n\nFor now, dropping entries when the queue fills will be disabled by default, but can be enabled, see below.\n\n### Configuration\n\nA new `command.background` configuration section controls the background processing behavior:\n\n```yaml\n  command:\n    background:\n      enabled: true           # Enable background processing (default: true)\n      queueSize: 1000        # Maximum queue size (default: 1000)\n      dropWhenFull: true     # Drop updates when queue is full (default: false)\n      delayThreshold: 50ms   # The threshold for how long updates are taking before starting to background updates\n```\n\nNote that the `commandRateLimiter` configuration section may instead be specified under `command` as `rateLimiter`.\n\nExample:\n\n```yaml\n  command:\n    background:\n      enabled: true\n      queueSize: 250\n      dropWhenFull: false\n      delayThreshold: 50ms\n    rateLimiter:\n      enabled:   true\n      maxQueued: 25\n```\n\nNote that if command rate limiter configuration is specified in both locations, the settings under `command` will take \nprecedence. The standalone `commandRateLimiter` section may be deprecated in the future.\n\n### Metrics\n\nWhen background processing is enabled, the following metrics are exposed:\n\n- command.background.queue_size - Current number of queued background tasks\n- command.background.worker_count - Current number of worker goroutines\n- command.background.busy_workers - Number of workers currently processing tasks\n- command.background.work_timer - Timer tracking background task execution (includes histogram, meter, and count)\n- command.background.dropped_entries - Count of dropped updates when queue is full (only when dropWhenFull is enabled)\n\n## New proxy.v1 Config Type\n\n*Originally released in 1.7.0*\n\nAdded support for dynamic service proxies with configurable binding and protocol options.\nThis allows Edge Routers and Tunnelers to create proxy endpoints that can forward traffic for Ziti services.\n\nThis differs from intercept.v1 in that intercept.v1 will intercept traffic on specified\nIP addresses or DNS entries to forward to a service using tproxy or tun interface,\ndepending on implementation.\n\nA proxy on the other hand will just start a regular TCP/UDP listener on the configured port,\nso traffic will have to be configured for that destination.\n\nExample proxy.v1 Configuration:\n\n```\n  {\n    \"port\": 8080,\n    \"protocols\": [\"tcp\"],\n    \"binding\": \"0.0.0.0\"\n  }\n```\n\nConfiguration Properties:\n  - port (required): Port number to listen on (1-65535)\n  - protocols (required): Array of supported protocols (tcp, udp)\n  - binding (optional): Interface to bind to. For the ER/T defaults to the configured lanIF config property.\n\nThis config type is currently supported by the ER/T when running in either proxy or tproxy mode.\n\n## Alert Events (BETA)\n\n*Originally released in 1.7.0*\n\nA new alert event type has been added to allow Ziti components to emit alerts for issues that network operators can address.\nAlert events are generated when components encounter problems such as service configuration errors or resource\navailability issues.\n\nAlert events include:\n  - Alert source type and ID (currently supports routers, with controller and SDK support planned for future releases)\n  - Severity level (currently supports error, with info and warning planned for future releases)\n  - Alert message and supporting details\n  - Related entities (router, identity, service, etc.) associated with the alert\n\nExample alert event when a router cannot bind a configured network interface:\n\n```\n  {\n    \"namespace\": \"alert\",\n    \"event_src_id\": \"ctrl1\",\n    \"timestamp\": \"2021-11-08T14:45:45.785561479-05:00\",\n    \"alert_source_type\": \"router\",\n    \"alert_source_id\": \"DJFljCCoLs\",\n    \"severity\": \"error\",\n    \"message\": \"error starting proxy listener for service 'test'\",\n    \"details\": [\n      \"unable to bind eth0, no address\"\n    ],\n    \"related_entities\": {\n      \"router\": \"DJFljCCoLs\",\n      \"identity\": \"DJFljCCoLs\",\n      \"service\": \"3DPjxybDvXlo878CB0X2Zs\"\n    }\n  }\n```\n\nAlert events can be consumed through the standard event system and logged to configured event handlers for monitoring and alerting purposes.\n\nThese events are currently in Beta, as the format is still subject to change. Once they've been in use in production for a while\nand proven useful, they will be marked as stable.\n\n## Azure Service Bus Event Sink\n\n*Originally released in 1.7.0. Contributed by @ffaraone.*\n\nAdds support for streaming controller events to Azure Service Bus.\nThe new logger enables real-time event streaming from the OpenZiti controller to Azure Service Bus\nqueues or topics, providing integration with Azure-based monitoring and analytics systems.\n\nTo enable the Azure Service Bus event logger, add configuration to the controller config file under the events section:\n\n```\n  events:\n    serviceBusLogger:\n      subscriptions:\n        - type: circuit\n        - type: session\n        - type: metrics\n          sourceFilter: .*\n          metricFilter: .*\n        # Add other event types as needed\n      handler:\n        type: servicebus\n        format: json\n        connectionString: \"Endpoint=sb://your-namespace.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=your-key\"\n        topic: \"ziti-events\"          # Use 'topic' for Service Bus topic\n        # queue: \"ziti-events-queue\"  # Or use 'queue' for Service Bus queue\n        bufferSize: 100                # Optional, defaults to 50\n```\n\n- Required configuration:\n    - format: Event format, currently supports only json\n    - connectionString: Azure Service Bus connection string\n    - Either topic or queue: Destination name (mutually exclusive)\n\n- Optional configuration:\n    - bufferSize: Internal message buffer size (default: 50)\n\n## OIDC is now enabled by default\n\nThe controller now automatically adds the `edge-oidc` API binding to any web listener that hosts\nthe `edge-client` API, even when `edge-oidc` is not explicitly listed in the `web` section of the\nconfiguration. This means OIDC-based authentication is available out of the box without requiring\nchanges to existing controller configurations.\n\n### Where OIDC binds\n\nThe `edge-oidc` binding is added to the same web listener(s) as `edge-client`. If `edge-client` is\nhosted on `127.0.0.1:1280`, the OIDC endpoints will be available on that same address under the\n`/oidc` path (e.g. `https://127.0.0.1:1280/oidc/.well-known/openid-configuration`).\n\nThe controller capabilities returned by `GET /version` will include `OIDC_AUTH` and the\n`edge-oidc` entry will be present in `apiVersions` when OIDC is active.\n\n### Opting out\n\nIf OIDC should not be enabled automatically, set `disableOidcAutoBinding: true` under `edge.api`:\n\n```yaml\nedge:\n  api:\n    address: 127.0.0.1:1280\n    sessionTimeout: 30m\n    disableOidcAutoBinding: true\n```\n\nWhen `disableOidcAutoBinding` is `true`, OIDC will only be active if `edge-oidc` is explicitly\nlisted as a binding in the `web` section. \n\nWhen OIDC is not enabled, all (SDK) clients will revert to using legacy authentication.\nLegacy authentication does not support multiple controllers or HA in general. Clients will treat\ntheir controller as if it is a single controller instance and will function as if no other controllers\nexist.\n\n\n## WWW-Authenticate Headers\n\nThe controller's Edge APIs now include `WWW-Authenticate` headers on `401 Unauthorized` responses,\nper issuer: https://github.com/openziti/ziti/issues/3356. These headers provide insight into why\na token was rejected. The main benefit of these headers is to convey information for JWT backed\nAPI Sessions and API Session authentication where a token may not have been provided (missing),\nis used beyond its expiration date (expired), or the token has become invalid for any other\nreason (invalid).\n\n`www-authenticate` headers are provided as a single header instance with multiple challenge values\nseparate by commas.\n\n### No Credentials Provided\n\nWhen a request hits a protected endpoint without any credentials, a single `WWW-Authenticate` header\nis returned listing both accepted auth schemes as comma-separated challenges:\n\n```\nWWW-Authenticate: zt-session realm=\"zt-session\" error=\"missing\" error_description=\"no matching token was provided\",Bearer realm=\"openziti-oidc\" error=\"missing\" error_description=\"no matching token was provided\"\n```\n\n### Token Errors\n\nWhen a token is present but cannot be accepted, the header identifies the scheme and what went wrong:\n\n```\nWWW-Authenticate: Bearer realm=\"openziti-oidc\" error=\"expired\" error_description=\"token expired\"\nWWW-Authenticate: Bearer realm=\"openziti-oidc\" error=\"invalid\" error_description=\"token is invalid\"\nWWW-Authenticate: zt-session realm=\"zt-session\" error=\"invalid\" error_description=\"token is invalid\"\n```\n\n### OIDC External JWT — Primary Authentication\n\nWhen an auth policy requires an external JWT signer for primary authentication (e.g., a PKCE flow\nbacked by an ext-jwt signer), the header identifies which signers are accepted and what went wrong.\nMultiple accepted signers are pipe-delimited in the `id` and `issuer` parameters:\n\n```\nWWW-Authenticate: Bearer realm=\"openziti-primary-ext-jwt\" error=\"missing\" error_description=\"no matching token was provided\" id=\"signer-id-1|signer-id-2\" issuer=\"https://issuer1.example.com|https://issuer2.example.com\"\n```\n\nThe `error` value follows the same `missing`/`expired`/`invalid` pattern as standard bearer token errors.\n\n### OIDC External JWT — Secondary / MFA Authentication\n\nWhen an auth policy requires an external JWT signer as a secondary factor (step-up after primary\nauth succeeds), the header identifies the single required signer. The `error` value follows the\nsame `missing`/`expired`/`invalid` pattern:\n\n```\nWWW-Authenticate: Bearer realm=\"openziti-secondary-ext-jwt\" error=\"missing\" error_description=\"no matching token was provided\" id=\"<signer-id>\" issuer=\"<issuer>\"\n```\n\n### Anonymous Endpoints\n\nUnauthenticated endpoints such as version information do not return `WWW-Authenticate` headers.\n\n## HA Preferred Leaders\n\nControllers can be marked as a preferred leader. \n\n**Example Config**\n```yaml\ncluster:\n  dataDir: /home/{{ .Model.MustVariable \"credentials.ssh.username\" }}/fablab/ctrldata\n  preferredLeader: true\n```\n\nIf a controller that is not marked preferredLeader becomes a preferredLeader, it will check \nif there's a node available that is marked as preferred. If there is one, or one later\njoins the cluster, the non-preferred node will attempt to transfer leadership to the \nnode that is marked as preferred.\n\n## Expanded Dynamic Cost Range\n\nThe dynamic cost range for smart routing has been expanded beyond the previous 64K limit. Under high\nload, terminators could saturate the cost space, making dynamic cost values meaningless for routing\ndecisions and leading to uneven load distribution. The expanded range allows for more granular cost\ndifferentiation even under heavy load.\n\n## Circuit ID and Error in Dial Failures\n\nDial failures now return the circuit ID and, when available, the error that caused the circuit to fail.\nPreviously, the circuit ID was only returned on successful dials. Note that SDKs will need to be\nupdated to surface the circuit id when a dial failure happens.\n\n## Multi-Underlay Control Channels\n\nRouter-to-controller control channels now support multiple underlays with priority-based message routing.\nThis allows time-sensitive control messages (heartbeats, routing, circuit requests) to be separated from\noperational data (metrics, inspections) across dedicated TCP connections, preventing bulk operations from\ndelaying user-affecting control plane traffic.\n\n## Dialing Identity Forwarded to Hosting SDK\n\nThe identity ID and name of the dialing client are now forwarded to the hosting SDK when a circuit is\nestablished. This allows hosting applications to identify which identity initiated the connection,\nenabling identity-aware request handling on the server side. This will require SDK updates to add this\nto the API for hosting applications.\n\n## Component Updates and Bug Fixes\n\n* github.com/openziti/channel/v4: [v4.2.41 -> v4.3.6](https://github.com/openziti/channel/compare/v4.2.41...v4.3.6)\n    * [Issue #228](https://github.com/openziti/channel/issues/228) - Ensure that Underlay never return nil on MultiChannel\n    * [Issue #226](https://github.com/openziti/channel/issues/226) - Allow specifying a minimum number of underlays for a channel, regardless of underlay type\n    * [Issue #225](https://github.com/openziti/channel/issues/225) - Add ChannelCreated to the UnderlayHandler API to allow handlers to be initialized with the channel before binding\n    * [Issue #224](https://github.com/openziti/channel/issues/224) - Update the underlay dispatcher to allow unknown underlay types to fall through to the default\n    * [Issue #222](https://github.com/openziti/channel/issues/222) - Allow injecting the underlay type into messages\n\n* github.com/openziti/edge-api: [v0.26.50 -> v0.27.0](https://github.com/openziti/edge-api/compare/v0.26.50...v0.27.0)\n    * [Issue #170](https://github.com/openziti/edge-api/issues/170) - Add preferredLeader flag to controllers\n    * [Issue #167](https://github.com/openziti/edge-api/issues/167) - Add ctrlChanListeners to router types\n    * [Issue #164](https://github.com/openziti/edge-api/issues/164) - Add permissions list to identity\n\n* github.com/openziti/foundation/v2: [v2.0.79 -> v2.0.88](https://github.com/openziti/foundation/compare/v2.0.79...v2.0.88)\n    * [Issue #472](https://github.com/openziti/foundation/issues/472) - Add support for multi-bit set/get to AtomicBitSet\n    * [Issue #464](https://github.com/openziti/foundation/issues/464) - Add support for -pre in versions\n\n* github.com/openziti/identity: [v1.0.118 -> v1.0.126](https://github.com/openziti/identity/compare/v1.0.118...v1.0.126)\n* github.com/openziti/metrics: [v1.4.2 -> v1.4.3](https://github.com/openziti/metrics/compare/v1.4.2...v1.4.3)\n    * [Issue #56](https://github.com/openziti/metrics/issues/56) - underlying resources of reference counted meters are not cleaned up when reference count hits zero\n\n* github.com/openziti/runzmd: [v1.0.84 -> v1.0.90](https://github.com/openziti/runzmd/compare/v1.0.84...v1.0.90)\n* github.com/openziti/sdk-golang: [v1.2.10 -> v1.5.1](https://github.com/openziti/sdk-golang/compare/v1.2.10...v1.5.1)\n    * [Issue #877](https://github.com/openziti/sdk-golang/issues/877) - Handle differences in xgress eof/end-of-circuit handling by adding a capabilities exchange\n    * [Issue #832](https://github.com/openziti/sdk-golang/issues/832) - Fuzz session refresh timers\n    * [Issue #879](https://github.com/openziti/sdk-golang/issues/879) - Return the connId in inspect response\n    * [Issue #878](https://github.com/openziti/sdk-golang/issues/878) - Fix responses from rx goroutines\n    * [Issue #874](https://github.com/openziti/sdk-golang/issues/874) - Add inspect support at the context level\n    * [Issue #871](https://github.com/openziti/sdk-golang/issues/871) - Make SDK better at sticking to MaxTerminator terminators\n    * [Issue #708](https://github.com/openziti/sdk-golang/issues/708) - Support for Go's built-in context in Dial methods\n    * [Issue #860](https://github.com/openziti/sdk-golang/issues/860) - Make the dialing identity's id and name available on dialed connections\n    * [Issue #857](https://github.com/openziti/sdk-golang/issues/857) - Use new error code and retry hints to correctly react to terminator errors\n    * [Issue #847](https://github.com/openziti/sdk-golang/issues/847) - Ensure the initial version check succeeds, to ensure we don't legacy sessions on ha or oidc-enabled controllers\n    * [Issue #824](https://github.com/openziti/sdk-golang/pull/824) - release notes and hard errors on no TOTP handler breaks partial auth events\n\n* github.com/openziti/secretstream: [v0.1.41 -> v0.1.47](https://github.com/openziti/secretstream/compare/v0.1.41...v0.1.47)\n* github.com/openziti/storage: [v0.4.31 -> v0.4.39](https://github.com/openziti/storage/compare/v0.4.31...v0.4.39)\n    * [Issue #122](https://github.com/openziti/storage/issues/122) - StringFuncNode has incorrect nil check, allowing panic\n    * [Issue #120](https://github.com/openziti/storage/issues/120) - Change post tx commit constraint handling order\n    * [Issue #119](https://github.com/openziti/storage/issues/119) - Add ContextDecorator API\n\n* github.com/openziti/transport/v2: [v2.0.198 -> v2.0.211](https://github.com/openziti/transport/compare/v2.0.198...v2.0.211)\n    * [Issue #31](https://github.com/openziti/transport/issues/31) - ipv6 Transport Address Parsing\n    * [Issue #149](https://github.com/openziti/transport/issues/149) - Archive transwarp code\n\n* github.com/openziti/xweb/v3: [v2.3.4 -> v3.0.3](https://github.com/openziti/xweb/compare/v2.3.4...v3.0.3)\n    * [Issue #32](https://github.com/openziti/xweb/issues/32) - watched identities sometimes don't reload when changed\n\n* github.com/openziti/ziti/v2: [v1.7.0 -> v2.0.0](https://github.com/openziti/ziti/compare/v1.7.0...v2.0.0)\n    * [Issue #3571](https://github.com/openziti/ziti/issues/3571) - Ensure 2.0 backwards compatibility with 1.6 and 1.5 using the smoketest\n    * [Issue #3636](https://github.com/openziti/ziti/issues/3636) - Adaptive rate limiter should use success rate rather than queue position\n    * [Issue #3600](https://github.com/openziti/ziti/issues/3600) - Add a preferredLeader flag, allow selected nodes to be preferred for raft leader, if they're available\n    * [Issue #3642](https://github.com/openziti/ziti/issues/3642) - Use xgress_common.Connection type for xgress_transport and xgress_proxy\n    * [Issue #3597](https://github.com/openziti/ziti/issues/3597) - Enable OIDC API by default\n    * [Issue #3624](https://github.com/openziti/ziti/issues/3624) - Multi-underlay control channel doesn't correctly handle lack of group secret on non-grouped underlays\n    * [Issue #3613](https://github.com/openziti/ziti/issues/3613) - Initializing cluster from existing db using `db:` config settings results in panic\n    * [Issue #3620](https://github.com/openziti/ziti/issues/3620) - Controller control channel heartbeats config parsing was erroneously nested under options parsing\n    * [Issue #3619](https://github.com/openziti/ziti/issues/3619) - Router connect events full sync interval was using min/max values meant for the batch interval\n    * [Issue #3618](https://github.com/openziti/ziti/issues/3618) - Router interfaceDiscovery.minReportInterval value was being set to checkInterval\n    * [Issue #3617](https://github.com/openziti/ziti/issues/3617) - Transit router disabled flag not passed through raft command structure\n    * [Issue #3333](https://github.com/openziti/ziti/issues/3333) - Updb user-lockout triggered by successful login attempts\n    * [Issue #3599](https://github.com/openziti/ziti/issues/3599) - Add gap detection and handling to router data model\n    * [Issue #3356](https://github.com/openziti/ziti/issues/3356) - Add WWW-Authenticate Headers\n    * [Issue #3074](https://github.com/openziti/ziti/issues/3074) - Dynamic cost range is too limited\n    * [Issue #3558](https://github.com/openziti/ziti/issues/3558) - terminator cost increased on egress dial success, not on circuit completion\n    * [Issue #3556](https://github.com/openziti/ziti/issues/3556) - global circuit costs not cleared when terminator is deleted\n    * [Issue #3557](https://github.com/openziti/ziti/issues/3557) - costing calculation for the weighted terminator selection strategy  is incorrect\n    * [Issue #2512](https://github.com/openziti/ziti/issues/2512) - Return circuit ID and error in dial failures\n    * [Issue #3569](https://github.com/openziti/ziti/issues/3569) - Version 2.0+ routers should not connect to controllers which do not support JWT formatted legacy sessions\n    * [Issue #3565](https://github.com/openziti/ziti/issues/3565) - Link dialer save 'is first conn' true, so all dials claim to be first, causing potential race condition\n    * [Issue #3550](https://github.com/openziti/ziti/issues/3550) - Support multi-underlay control channels\n    * [Issue #3535](https://github.com/openziti/ziti/issues/3535) - Remove the legacy xgress_edge_tunnel implementation\n    * [Issue #3547](https://github.com/openziti/ziti/issues/3547) - Add support for sending the dialing identity id and name to the hosting sdk\n    * [Issue #3541](https://github.com/openziti/ziti/issues/3541) - Remove option to disable the router data model in the controller\n    * [Issue #3540](https://github.com/openziti/ziti/issues/3540) - Handle UDP difference between proxy and tproxy implementations\n    * [Issue #3527](https://github.com/openziti/ziti/issues/3527) - ER/T UDP tunnels keep closed connections for 30s, preventing potential new good connections in that time\n    * [Issue #3526](https://github.com/openziti/ziti/issues/3526) - ER/T half-close logic is incorrect\n    * [Issue #3524](https://github.com/openziti/ziti/issues/3524) - Provide more error context to SDKs for terminator errors\n    * [Issue #3509](https://github.com/openziti/ziti/issues/3509) - Enforce policy on the router for oidc sessions, by closing open circuits and terminators when service access is lost\n    * [Issue #3531](https://github.com/openziti/ziti/issues/3531) - Remove created/updated/deleted terminator events. Obsoleted by entity change events.\n    * [Issue #3532](https://github.com/openziti/ziti/issues/3532) - Removed deprecated create identity <type> subcommands\n    * [Issue #3521](https://github.com/openziti/ziti/issues/3521) - Cleanup CLI to remove calls to os.Exit to be embed friendlier\n    * [Issue #3516](https://github.com/openziti/ziti/issues/3516) - Remove support for create terminator v1\n    * [Issue #3512](https://github.com/openziti/ziti/issues/3512) - Remove legacy link management code from the controller\n    * [Issue #3511](https://github.com/openziti/ziti/issues/3511) - router proxy mode fails to resolve interface if binding is 0.0.0.0\n    * [Issue #3503](https://github.com/openziti/ziti/issues/3503) - Allow routers to request current cluster membership information\n    * [Issue #3501](https://github.com/openziti/ziti/issues/3501) - Get cluster membership information from raft directly, rather than trying to cache it in the DB\n    * [Issue #3500](https://github.com/openziti/ziti/issues/3500) - Set a router data model timeline when initializing a new HA setup, rather than letting it stay blank\n    * [Issue #3504](https://github.com/openziti/ziti/issues/3504) - Reduce router data model full state updates\n    * [Issue #3492](https://github.com/openziti/ziti/pull/3492) - Bump openziti/ziti-console-assets from 3.12.9 to 4.0.0 in /dist/docker-images/ziti-controller in the all group\n    * [Issue #3484](https://github.com/openziti/ziti/issues/3484) - router ctrl channel handler for handling cluster changes has an initialization race condition\n    * [Issue #3477](https://github.com/openziti/ziti/issues/3477) - Optionally enable model changes triggered by login to be non-blocking and to be droppable if the system is under load\n    * [Issue #3473](https://github.com/openziti/ziti/issues/3473) - Enable tls handshake rate limiter by default and tweak default values.\n    * [Issue #3471](https://github.com/openziti/ziti/issues/3471) - Go tunneler is ignoring host config MaxConnections\n    * [Issue #3469](https://github.com/openziti/ziti/issues/3469) - Only send model updates on resubscribe if the RDM index has advanced\n    * [Issue #2573](https://github.com/openziti/ziti/issues/2573) - An edge router in a tight restart loop causes a resource leak on routers to which it connects.\n    * [Issue #3430](https://github.com/openziti/ziti/issues/3430) - Add permissions list to identity\n    * [Issue #2109](https://github.com/openziti/ziti/issues/2109) - Add Edge Management Read Only Capability\n    * [Issue #3435](https://github.com/openziti/ziti/issues/3435) - Add edge management API permissions by entity type and action\n    * [Issue #3441](https://github.com/openziti/ziti/issues/3441) - Update router connection tracker to interrogate active connections\n    * [Issue #3451](https://github.com/openziti/ziti/issues/3451) - ci - compare only stable releases when promoting\n    * [Issue #3437](https://github.com/openziti/ziti/issues/3437) - SDK OIDC token updates to routers should return an error if invalid\n    * [Issue #3348](https://github.com/openziti/ziti/issues/3348) - Unable to clear/reset the \"tags\" property on an entity to an empty object\n    * [Issue #3452](https://github.com/openziti/ziti/issues/3452) - `ziti agent cluster add` has bad behavior if the add address doesn't match the advertise address\n    * [Issue #3410](https://github.com/openziti/ziti/issues/3410) - Consolidate fabric REST API code with edge management and edge client code\n    * [Issue #3425](https://github.com/openziti/ziti/issues/3425) - RDM not properly responding to tunneler enabled flag changes\n    * [Issue #3420](https://github.com/openziti/ziti/issues/3420) - The terminator id cache uses the same id for all terminators in a host.v2 config, resulting in a single terminator\n    * [Issue #3419](https://github.com/openziti/ziti/issues/3419) - When using the router data model, precedence specified on the per-service identity mapping are incorrectly interpreted\n    * [Issue #3318](https://github.com/openziti/ziti/issues/3318) - Terminator creation seems to slow exponentially as the number of terminators rises from 10k to 20k to 40k\n    * [Issue #3407](https://github.com/openziti/ziti/issues/3407) - The CLI doesn't properly pass JWT authentication information to websocket endpoints\n    * [Issue #3359](https://github.com/openziti/ziti/issues/3359) - Ensure router data model subscriptions have reasonable performance and will scale\n    * [Issue #3354](https://github.com/openziti/ziti/issues/3354) - SDK/ENV Info from SDKs is not distributed in HA\n    * [Issue #3381](https://github.com/openziti/ziti/issues/3381) - the fabric service REST apis are missing the maxIdleTime property\n    * [Issue #3382](https://github.com/openziti/ziti/issues/3382) - Legacy service sessions generated pre-1.7.x are incompatible with v1.7.+ and need to be cleared\n    * [Issue #3339](https://github.com/openziti/ziti/issues/3339) - get router ctrl.endpoint from ctrls claim in JWT\n    * [Issue #3378](https://github.com/openziti/ziti/issues/3378) - login with file stopped working\n    * [Issue #3349](https://github.com/openziti/ziti/issues/3349) - UPDB OIDC login returns wrong content type\n    * [Issue #2324](https://github.com/openziti/ziti/issues/2324) - Add Ext-JWT/OIDC enrollment\n    * [Issue #3346](https://github.com/openziti/ziti/issues/3346) - Fix confusing attempt logging\n    * [Issue #3337](https://github.com/openziti/ziti/issues/3337) - Router reports \"no xgress edge forwarder for circuit\"\n    * [Issue #3345](https://github.com/openziti/ziti/issues/3345) - Clean up connect events tests and remove global XG registry\n    * [Issue #3264](https://github.com/openziti/ziti/issues/3264) - Allow routers to generate alert events in cases of service misconfiguration\n\n\n","mentions_count":1},{"url":"https://api.github.com/repos/openziti/ziti/releases/292581548","assets_url":"https://api.github.com/repos/openziti/ziti/releases/292581548/assets","upload_url":"https://uploads.github.com/repos/openziti/ziti/releases/292581548/assets{?name,label}","html_url":"https://github.com/openziti/ziti/releases/tag/v2.0.0-pre2","id":292581548,"author":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"node_id":"RE_kwDODVFMN84RcHCs","tag_name":"v2.0.0-pre2","target_commitish":"main","name":"v2.0.0-pre2","draft":false,"immutable":false,"prerelease":true,"created_at":"2026-03-03T14:38:40Z","updated_at":"2026-03-03T14:53:59Z","published_at":"2026-03-03T14:53:59Z","assets":[{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/366118432","id":366118432,"node_id":"RA_kwDODVFMN84V0oYg","name":"checksums.sha256.txt","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"text/plain; charset=utf-8","state":"uploaded","size":790,"digest":"sha256:ad249bc90a7495e933ef7f25f8ce0fadf4165728afae75163ac5d48af320089f","download_count":4,"created_at":"2026-03-03T14:53:58Z","updated_at":"2026-03-03T14:53:58Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre2/checksums.sha256.txt"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/366118424","id":366118424,"node_id":"RA_kwDODVFMN84V0oYY","name":"sbom-v2.0.0-pre2.spdx.json","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/json","state":"uploaded","size":1033507,"digest":"sha256:dce58344dde284f76ffde799a4dd241d98ff177c389dd2472c6278e49373345b","download_count":3,"created_at":"2026-03-03T14:53:57Z","updated_at":"2026-03-03T14:53:57Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre2/sbom-v2.0.0-pre2.spdx.json"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/366118426","id":366118426,"node_id":"RA_kwDODVFMN84V0oYa","name":"source-v2.0.0-pre2.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":3710622,"digest":"sha256:1618f844c63abbb3afd59bd45ef22cfef16745582420c914a5ed018085f3cfc5","download_count":6,"created_at":"2026-03-03T14:53:57Z","updated_at":"2026-03-03T14:53:58Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre2/source-v2.0.0-pre2.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/366118360","id":366118360,"node_id":"RA_kwDODVFMN84V0oXY","name":"ziti-darwin-amd64-2.0.0-pre2.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":54131847,"digest":"sha256:b2d975639f16252e9a27050b5a8420882dfcc07c48b0137f180e656a75501e04","download_count":7,"created_at":"2026-03-03T14:53:54Z","updated_at":"2026-03-03T14:53:57Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre2/ziti-darwin-amd64-2.0.0-pre2.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/366118359","id":366118359,"node_id":"RA_kwDODVFMN84V0oXX","name":"ziti-darwin-arm64-2.0.0-pre2.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":50444489,"digest":"sha256:1b411f1ecbf0db040a7f379a01bc8e158802bffc7c8f224de1dc32b482d111a9","download_count":4,"created_at":"2026-03-03T14:53:54Z","updated_at":"2026-03-03T14:53:57Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre2/ziti-darwin-arm64-2.0.0-pre2.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/366118422","id":366118422,"node_id":"RA_kwDODVFMN84V0oYW","name":"ziti-linux-amd64-2.0.0-pre2.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":55442778,"digest":"sha256:0fd258a72bde81f3d4e474c9b5d4e1bbc15d5b45b845828b7544ad758872bf96","download_count":21,"created_at":"2026-03-03T14:53:57Z","updated_at":"2026-03-03T14:53:58Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre2/ziti-linux-amd64-2.0.0-pre2.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/366118363","id":366118363,"node_id":"RA_kwDODVFMN84V0oXb","name":"ziti-linux-arm-2.0.0-pre2.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":51861569,"digest":"sha256:fe5283ee0704efb7e8ca512ac41c5004b2c1d07e5b8b0f53ca440bb3f0298077","download_count":7,"created_at":"2026-03-03T14:53:55Z","updated_at":"2026-03-03T14:53:57Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre2/ziti-linux-arm-2.0.0-pre2.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/366118357","id":366118357,"node_id":"RA_kwDODVFMN84V0oXV","name":"ziti-linux-arm64-2.0.0-pre2.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":51948937,"digest":"sha256:d8527599098beb7b8a68d026902dea48d564ce43a2a64d04d459b9a16b63309f","download_count":6,"created_at":"2026-03-03T14:53:54Z","updated_at":"2026-03-03T14:53:57Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre2/ziti-linux-arm64-2.0.0-pre2.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/366118358","id":366118358,"node_id":"RA_kwDODVFMN84V0oXW","name":"ziti-windows-amd64-2.0.0-pre2.zip","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/zip","state":"uploaded","size":44826779,"digest":"sha256:c40f42b2a8e7a077dbc03b48635aedcdfbd8323a0ab74284abb19cf6e141cd82","download_count":7,"created_at":"2026-03-03T14:53:54Z","updated_at":"2026-03-03T14:53:57Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-pre2/ziti-windows-amd64-2.0.0-pre2.zip"}],"tarball_url":"https://api.github.com/repos/openziti/ziti/tarball/v2.0.0-pre2","zipball_url":"https://api.github.com/repos/openziti/ziti/zipball/v2.0.0-pre2","body":"# Release 2.0.0\n\n## What's New\n\nThis is the next major version release of OpenZiti, following the 1.0 release in April 2024. \nOf particular note is that HA controllers are now considered ready for general use. \nThis release also introduces a new permissions model, OIDC/JWT token-based enrollment, \nclustering performance improvements, and a number of other features and fixes. Because \nsome of these changes are not backwards compatible with older routers, we're marking this \nas a major version bump.\n\n### HA Controllers are now considered ready for general use\n\nThis is a pretty big milestone and marks the completion of work that's been ongoing for a couple of years.\nThe HA work has brought with it some notable changes. Authentication now uses JWTs by default. Using JWTs\nmeans the controllers don't need to store session and propagate them to the routers. This removes a bottleneck\nfrom the network and allows the load to be more easily distributed among controllers and routers.\n\nTo support distributed authentication, the routers now get a bespoke version of the data model. In addition\nto enabling distributed authentication this will allow us to remove the need for service polling and further \nreduce the load on controllers in the future.\n\n### Router Compatibility\n\nRelated to the JWT work, routers with version 2.+ will only work with controllers that are version 2.+. This means\nto upgrade your network, controllers should be upgraded first. Routers can then be upgraded individually.\n\n2.x routers should still work fine with older router versions.\n\nWe try very hard to avoid breaking changes like this, but sometimes the engineering trade-offs lead there. This change\nwas first made in the 1.7 release. That release has not been marked stable, and we have no plans to do so, because\nof the backwards incompatibility.\n\n### New Permissions Model (BETA)\n\nAs one feature goes out of beta, another arrives into beta. This release introduces a new permissions system\nfor more fine grained control to the management API. It's not expected to change, but may do so based on feedback\nfrom users.\n\n### Updated Release Process\n\nWe have moved to creating `-preN` releases for major and minor versions. This way we can put out release candidates,\nor feature previews and put them through internal testing and let interested folks from the community try them out.\nThen, when we're ready, we can run the full validation suite against the last pre-release and retag it. \n\nPatch releases won't have `-preN` and should contain only high priority bug fixes.\n\n### Deprecation Cleanup\n\nSince we already have a breaking change, we're removing some other backwards compatibility code.\n\n* Controller managed links \n    * Router managed links were introduced in v0.30.0. \n    * If you're upgrading from an older versions, you'll want to upgrade to the latest 1.x release before jumping to 2.x\n    * Github tracking issue: https://github.com/openziti/ziti/issues/3512\n* `ziti edge create identity <type>`\n    * Identity types other than router were removed in v0.30.2\n    * The `type` can be dropped from the CLI command\n    * Github tracking issue: https://github.com/openziti/ziti/issues/3532\n* Terminator create/update/delete events\n    * These have been superseded by entity change events, which also have create/update/delete events for terminators\n    * Entity change events were introduced in v0.28.0\n    * Github tracking issue: https://github.com/openziti/ziti/issues/3531\n* `xgress_edge_tunnel` v1\n    * This is the first implementation of the tunneler in edge-router code (ER/T) which used legacy api sessions and services\n    * The v2 version uses the router data model and was introduced in v0.30.x\n    * Github tracking issue: https://github.com/openziti/ziti/issues/3516\n\n### Legacy Session Deprecation\n\nOIDC sessions are now preferred. They are the default, or will become the default for SDKs and tunnelers. They are also required\nwhen running HA. Legacy API and service session are now deprecated and will be removed in the OpenZiti v3.0.0 release. \n\n### Additional Features\n\n* Controllers can now optionally bind APIs using an OpenZiti identity\n* `ziti edge login` now supports the `--network-identity` flag to authenticate and establish connections through the Ziti overlay network\n* `ziti edge login` now supports using a bearer token with `--token` for authentication. The token is expected to be\n  provided as just the JWT, not with the \"Bearer \" prefix\n* Identity configuration can now be loaded from files or environment variables for flexible deployment scenarios\n* Identities can now be provisioned just-in-time through OIDC/JWT token-based enrollment\n* Multiple model updates can now be in-flight at the same time, improving clustering performance\n* Authentication-related model updates can now be non-blocking and even dropped if the system is too busy\n* Routers now provide more error context to SDKs for terminator errors, enabling better retry behavior\n* New `proxy.v1` config type for dynamic service proxies (originally released in 1.7.0)\n* New alert event type for surfacing operational issues to network operators - Beta (originally released in 1.7.0)\n* New Azure Service Bus event sink for streaming controller events, contributed by @ffaraone (originally released in 1.7.0)\n* Bundled ZAC upgraded to 4.0\n* Build updated to Go 1.25\n* CLI cleaned up to remove calls to `os.Exit`, making it more friendly for embedding\n* Controller Edge APIs now return `WWW-Authenticate` response headers on `401 Unauthorized` responses, giving clients actionable information about which auth methods are accepted and what went wrong\n* HA Controllers can be marked as 'preferredLeader' via config\n* Dynamic cost range for smart routing expanded beyond the previous 64K limit\n* Dial failures now return the circuit ID and error information for easier debugging\n* Router-to-controller control channels now support multiple underlays with priority-based message routing\n* The dialing identity's ID and name are now forwarded to the hosting SDK\n\n## Basic Permission System (BETA)\n\nAdded a basic permission system that allows more control over identity access to controller management API operations. \nThis replaces the previous binary admin/non-admin model with a more flexible permission system.\n\n**NOTE:** This feature is in BETA, primarily so we can get feedback on which permissions make sense. The implementation is unlikely to change\nbut the set of exposed permissions may grow, shrink or change based on user feedback. \n\n### Permission Model\n\nThe permission system supports three levels of authorization:\n\n  1. **Global Permissions**: System-wide access levels\n     - `admin` - Full access to all operations. This is still controlled by the `isAdmin` flag on identity\n     - `admin_readonly` - Read-only access to all resources except debugging facilities inspect and validate\n\n  2. **Entity-Level Permissions**: Full CRUD access to specific entity types\n     - Granting an entity-level permission (e.g., `service`) provides complete create, read, update, and delete access for that entity type\n\n  3. **Action-Level Permissions**: Specific operation access on entity types\n     - Fine-grained control using the pattern `<entity>.<action>` (e.g., `service.read`, `identity.update`)\n     - Supports `create`, `read`, `update`, and `delete` actions per entity type\n\n### Supported Entity Permissions\n\nThe following entity-level permissions are available:\n\n- `auth-policy` - Authentication policy management\n- `ca` - Certificate Authority management\n- `config` - Configuration management\n- `config-type` - Configuration type management\n- `edge-router-policy` - Edge router policy management\n- `enrollment` - Enrollment management\n- `external-jwt-signer` - External JWT signer management\n- `identity` - Identity management\n- `posture-check` - Posture check management\n- `router` - Edge and transit router management\n- `service` - Service management\n- `service-policy` - Service policy management\n- `service-edge-router-policy` - Service edge router policy management\n- `terminator` - Terminator management\n- `ops` - Operational resources (API sessions, sessions, circuits, links, inspect and validate)\n\n### Permission Assignment\n\nPermissions are assigned to identities via the `permissions` field in the identity resource. Multiple permissions can be granted to a single identity, and permissions are additive.\n\n### Cross-Entity Operations\n\nListing related entities through an entity's endpoints requires appropriate permissions for the related entity type. For example:\n- Listing services for a service-policy requires `service.read` permission\n- Listing identities for an edge-router-policy requires `identity.read` permission\n- Listing configs for a service requires `config.read` permission\n\n**NOTE:** \nMore permissions than expected may be required when performing actions through the CLI or ZAC. Take for example, when an identity \nhas `config.create` and is attempting to create a new config. The CLI may fail if the identity doesn't have `config-type.read`\nas well because it will need to look up the config type id that corresponds to the given config type name.\n\nSimilar cross entity read permissions may be required when creating services.\n\n### Admin Protection\n\nNon-admin identities cannot:\n- Create identities with the `isAdmin` flag\n- Create identities with any permissions granted\n- Modify admin-related fields on existing identities\n- Update or delete admin identities\n- Grant permissions to identities\n\nThese protections ensure that privilege escalation is prevented and admin access remains controlled.\n\n\n## Binding Controller APIs With Identity\n\nController APIs can now be bound to an OpenZiti overlay network identity, allowing secure communication through\nthe Ziti network. This is useful for scenarios where you want to expose controller APIs only through the overlay\nnetwork rather than on a standard network interface.\n\n### Configuration Structure\n\nA standard `bindPoint` configuration looks like this:\n```text\n    bindPoints:\n      - interface: 127.0.0.1:18441\n        address: 127.0.0.1:18441\n```\n\nTo bind controller APIs to an OpenZiti identity, add an additional `identity` block to your `bindPoints`. The\nidentity configuration specifies where to load the Ziti identity file and which service to bind it to:\n\n```text\n    bindPoints:\n      - interface: 127.0.0.1:18441\n        address: 127.0.0.1:18441\n      - identity:\n          file: \"c:/temp/ctrl.testing/ctrl.identity.json\"\n          service: \"mgmt\"\n```\n\n### Supported Configuration Options\n\n- `file`: Path to a Ziti identity JSON file containing the controller's identity and enrollment certificate\n- `env`: Name of an environment variable containing a base64-encoded Ziti identity (alternative to `file`)\n- `service`: The name of the Ziti service to bind the controller API to\n\n### Using Environment Variables\n\nFor deployments where storing identity files on disk is not preferred, you can reference a base64-encoded\nidentity file from an environment variable. The environment variable should contain the base64-encoded contents\nof the identity JSON file.\n\nFor example, if an environment variable named `ZITI_CTRL_IDENTITY` contains a base64-encoded identity file:\n\n```text\n    bindPoints:\n      - interface: 127.0.0.1:18441\n        address: 127.0.0.1:18441\n      - identity:\n          env: ZITI_CTRL_IDENTITY\n          service: \"mgmt\"\n```\n\n### IPv6 Support\n\nBoth IPv4 and IPv6 addresses are supported for standard bind points. IPv6 addresses should be specified in bracket\nnotation with a port number:\n\n```text\n    bindPoints:\n      - interface: \"[::1]:18441\"\n        address: \"[::1]:18441\"\n      - identity:\n          file: \"/path/to/identity.json\"\n          service: \"mgmt\"\n```\n\n## CLI Enhancements for Identity-Based Connections\n\nThe `ziti edge login` command and REST client utilities have been enhanced to support identity-based connections\nthrough the Ziti overlay network.\n\n### New `--network-identity` Flag for `ziti edge login`\n\nThe `ziti edge login` command now includes a `--network-identity` flag that allows you to authenticate to a Ziti\ncontroller through the overlay network using a Ziti identity:\n\n```bash\nziti edge login https://ziti.mgmt.apis.local:1280 \\\n  --username myuser \\\n  --password mypass \\\n  --network-identity /path/to/identity.json\n```\n\nThis is useful when the controller is only accessible through the Ziti overlay network or when you want to ensure\nall communication to the controller flows through the overlay for security purposes.\n\n### Identity Resolution Order\n\nWhen establishing connections, identities are resolved in the following order:\n\n1. **Command-line flag**: The `--network-identity` flag takes precedence\n2. **Environment variable**: If `ZITI_CLI_NETWORK_ID` is set and contains a base64-encoded identity, it is used\n3. **Cached identity file**: If a network identity was saved from a previous login in the Ziti config directory, it may be used\n\nThis layered approach allows for flexibility in deployment scenarios:\n- Development: Use command-line flags for quick testing\n- Automation: Use environment variables in CI/CD pipelines\n- Production: Cache identities securely for repeated access\n\n#### Dialing Modes When Authenticating\n\nThe CLI supports two dialing modes:\n\n**Intercept-based Dialing (Default)**\nBy default, URLs are expected to leverage intercepts. Create a service with an appropriate intercept config and use\nthe intercept address when dialing. This is the standard mode for most use cases. For example, given a service with\nthe intercept `ziti.mgmt.apis.local`\n```bash\nziti edge login https://ziti.mgmt.apis.local:1280 \\\n  --username myuser \\\n  --password mypass \\\n  --network-identity /path/to/identity.json\n```\n\n**Identity-aware Dialing (Addressable Terminators)**\nTo support addressable terminators-based dialing, specify a user in the URL. This activates dial-by-identity\nfunctionality. The URL format should be `identity-to-dial@service-name-to-dial`. For example:\n```bash\nziti edge login https://my-identity@my-service:1280 \\\n  --username myuser \\\n  --password mypass \\\n  --network-identity /path/to/identity.json\n```\n\nIn this mode, the transport extracts the identity from the URL and uses it to establish a direct connection to\nthe specified service via the addressable terminator.\n\n\n## OIDC/JWT Token-based Enrollment\n\nOpenZiti now supports provisioning identities just-in-time through OIDC/JWT token enrollment. External identity \nproviders can be configured to allow identities to enroll using JWT tokens, with support for the resulting \nidentities to use certificate or token authentication.\n\n### External JWT Signer Configuration\n\nExternal JWT signers are configured via the Edge Management API to define enrollment behavior with the following new \nenrollment-specific properties:\n\n- **enrollToCertEnabled** - When enabled, identities can exchange a JWT token and a certificate signing request (CSR) \n        for a client certificate during enrollment. The certificate can then be used for standard certificate-based\n        authentication.\n\n- **enrollToTokenEnabled** - When enabled, identities can use a JWT token to enroll. The current token or future tokens\n        may be used for authentication.\n\n- **enrollNameClaimsSelector** - Specifies which JWT claim contains the identity name. Accepts a JSON pointer \n        (e.g., `/preferred_username`) or a simple property name (e.g., `preferred_username`, automatically converted to\n        `/preferred_username`). Defaults to `/sub` if not specified. The extracted value becomes the identity name in Ziti.\n\n- **enrollAttributeClaimsSelector** - Specifies which JWT claims to extract as identity attributes during enrollment.\n        Accepts a JSON pointer (e.g., `/roles`) or a simple property name (e.g., `roles`). Extracted attributes are \n        applied to the newly enrolled identity for use in authorization policies.\n\n- **enrollAuthPolicyId** - Specifies the authentication policy to apply to newly enrolled identities. This determines\n        what authentication methods are available for the identity post-enrollment.\n\nAdditionally the existing property named **claimsProperty** that specifies external id to match identities to:\n\n- now supports a JSON pointer (e.g., `/id`) or a simple property name (e.g., `id`)\n- is used to populate the `externalId` field of the identity\n\n### Enrollment Paths\n\n#### Certificate Enrollment (enrollToCertEnabled)\n\nWhen certificate enrollment is enabled, unauthenticated users can:\n\n1. Obtain a list of available IdPs from the public Edge Client API `GET /external-jwt-signers` endpoint, where \n   `enrollToCertEnabled` is set to `true`\n2. Obtain a JWT from the configured OIDC provider\n3. Generate a certificate signing request (CSR)\n4. Submit an enrollment request with the JWT and CSR\n5. Have their identity created in Ziti with attributes extracted from JWT claims\n6. Receive a signed client certificate for certificate-based authentication\n\n#### Token Enrollment (enrollToTokenEnabled)\n\nWhen token enrollment is enabled, unauthenticated users can:\n\n1. Obtain a list of available IdPs from the public Edge Client API `GET /external-jwt-signers` endpoint, where \n   `enrollToTokenEnabled` is set to `true`\n1. Obtain a JWT from the configured OIDC provider\n2. Submit an enrollment request with the JWT\n3. Have their identity created in Ziti with attributes extracted from JWT claims\n4. Receive a Ziti API token for token-based authentication\n\n### Edge Management API\n\nThe Edge Management API provides full CRUD operations for configuring external JWT signers:\n\n- `POST /external-jwt-signers` - Create a new external JWT signer with all configuration options\n- `GET /external-jwt-signers` - List all configured external JWT signers\n- `GET /external-jwt-signers/{id}` - Retrieve a specific signer configuration\n- `PUT /external-jwt-signers/{id}` - Update all fields of a signer\n- `PATCH /external-jwt-signers/{id}` - Partially update a signer\n- `DELETE /external-jwt-signers/{id}` - Delete a signer\n\n### Edge Client API\n\nThe Edge Client API exposes a reduced set of external JWT signer information for unauthenticated enrollment requests:\n\n- `GET /external-jwt-signers` - List available JWT signers with enrollment capabilities\n\nThe client API response includes the following fields for each signer:\n\n- `name` - Signer name\n- `externalAuthUrl` - URL where users obtain JWT tokens\n- `clientId` - OIDC client ID\n- `scopes` - Requested OIDC scopes\n- `openIdConfigurationUrl` - OIDC discovery endpoint\n- `audience` - Expected token audience\n- `targetToken` - Token type to use (ACCESS or ID)\n- **`enrollToCertEnabled`** - Flag indicating certificate enrollment is available\n- **`enrollToTokenEnabled`** - Flag indicating token enrollment is available\n\n### CLI Commands\n\n**Create an external JWT signer with enrollment options:**\n```\nziti edge controller create ext-jwt-signer <name> <issuer> \\\n  --jwks-endpoint <url> \\\n  --audience <audience> \\\n  --enroll-to-cert \\\n  --enroll-to-token=false \\\n  --enroll-name-claims-selector preferred_username \\\n  --enroll-attr-claims-selector roles \\\n  --enroll-auth-policy <policy-id-or-name>\n```\n\n**Update enrollment options on an existing signer:**\n```\nziti edge controller update ext-jwt-signer <name|id> \\\n  --enroll-to-cert \\\n  --enroll-auth-policy <policy-id-or-name>\n```\n\n**List external JWT signers:**\n```\nziti edge controller list ext-jwt-signers\n```\n\n## Clustering Performance Improvements\n\nIn previous releases, model updates were submitted to raft one at at time. This prevented \nraft from being efficient by allowing command batching. This release allows multiple \nmodel updates to be in-flight at the same time. \n\nNew Configuration Options\n\n1. Raft Apply Timeout (raft.applyTimeout)\n\nLocation: Controller configuration file, under raft section\nType: Duration\nDefault: 5s\nDescription: Timeout for applying commands to the Raft distributed log. Commands that exceed this timeout will trigger adaptive rate limiter backoff.\n\nExample:\n```\n  raft:\n    applyTimeout: 10s\n```\n\n2. Raft Rate Limiter Configuration (raft.rateLimiter)\n\nA new adaptive rate limiter that controls the submission of commands to the Raft cluster. Unlike the existing command rate limiter, this specifically manages in-flight Raft operations with adaptive window sizing.\n\nConfiguration Structure:\n```\n  raft:\n    rateLimiter:\n      enabled: true\n      minSize: 5\n      maxSize: 250\n      timeout: 30s\n```\n\nSub-options:\n\n  - enabled (boolean)\n    - Default: true\n    - Description: Enable/disable adaptive rate limiting for Raft command submission\n  - minSize (integer)\n    - Default: 5\n    - Minimum: 1\n    - Description: Minimum window size for concurrent in-flight Raft operations\n  - maxSize (integer)\n    - Default: 250\n    - Description: Maximum window size for concurrent in-flight Raft operations. Must be >= minSize\n  - timeout (duration)\n    - Default: 30s\n    - Description: Time after which outstanding work is assumed to have failed if not marked completed\n\n3. Restart Self on Snapshot (raft.restartSelfOnSnapshot)\n\nLocation: Controller configuration file, under raft section\nType: Boolean\nDefault: false\nDescription: When true, the controller will automatically restart itself when restoring a snapshot to an initialized system. When false, the controller will exit with code 0, requiring external process management to restart it.\n\nExample:\n```\n  raft:\n    restartSelfOnSnapshot: true\n```\n\n### New Metrics\n\nThe adaptive rate limiter exposes three new metrics:\n\n  1. raft.rate_limiter.queue_size (gauge)\n    - Current number of operations queued/in-flight\n  2. raft.rate_limiter.work_timer (timer)\n    - Duration of rate-limited operations\n  3. raft.rate_limiter.window_size (gauge)\n    - Current adaptive window size\n\n## Background Processing for Identity Updates\n\nIdentity environment and authenticator updates that occur during authentication are now processed asynchronously in the background. \nThis prevents authentication requests from blocking when the system is under load, significantly improving resilience during thundering herd scenarios.\n\nWhen the background queue fills up, updates can be dropped as they will be refreshed on the next authentication attempt. \nThis allows the system to gracefully handle load spikes without impacting authentication performance.\n\nFor now, dropping entries when the queue fills will be disabled by default, but can be enabled, see below.\n\n### Configuration\n\nA new `command.background` configuration section controls the background processing behavior:\n\n```yaml\n  command:\n    background:\n      enabled: true           # Enable background processing (default: true)\n      queueSize: 1000        # Maximum queue size (default: 1000)\n      dropWhenFull: true     # Drop updates when queue is full (default: false)\n      delayThreshold: 50ms   # The threshold for how long updates are taking before starting to background updates\n```\n\nNote that the `commandRateLimiter` configuration section may instead be specified under `command` as `rateLimiter`.\n\nExample:\n\n```yaml\n  command:\n    background:\n      enabled: true\n      queueSize: 250\n      dropWhenFull: false\n      delayThreshold: 50ms\n    rateLimiter:\n      enabled:   true\n      maxQueued: 25\n```\n\nNote that if command rate limiter configuration is specified in both locations, the settings under `command` will take \nprecedence. The standalone `commandRateLimiter` section may be deprecated in the future.\n\n### Metrics\n\nWhen background processing is enabled, the following metrics are exposed:\n\n- command.background.queue_size - Current number of queued background tasks\n- command.background.worker_count - Current number of worker goroutines\n- command.background.busy_workers - Number of workers currently processing tasks\n- command.background.work_timer - Timer tracking background task execution (includes histogram, meter, and count)\n- command.background.dropped_entries - Count of dropped updates when queue is full (only when dropWhenFull is enabled)\n\n## New proxy.v1 Config Type\n\n*Originally released in 1.7.0*\n\nAdded support for dynamic service proxies with configurable binding and protocol options.\nThis allows Edge Routers and Tunnelers to create proxy endpoints that can forward traffic for Ziti services.\n\nThis differs from intercept.v1 in that intercept.v1 will intercept traffic on specified\nIP addresses or DNS entries to forward to a service using tproxy or tun interface,\ndepending on implementation.\n\nA proxy on the other hand will just start a regular TCP/UDP listener on the configured port,\nso traffic will have to be configured for that destination.\n\nExample proxy.v1 Configuration:\n\n```\n  {\n    \"port\": 8080,\n    \"protocols\": [\"tcp\"],\n    \"binding\": \"0.0.0.0\"\n  }\n```\n\nConfiguration Properties:\n  - port (required): Port number to listen on (1-65535)\n  - protocols (required): Array of supported protocols (tcp, udp)\n  - binding (optional): Interface to bind to. For the ER/T defaults to the configured lanIF config property.\n\nThis config type is currently supported by the ER/T when running in either proxy or tproxy mode.\n\n## Alert Events (BETA)\n\n*Originally released in 1.7.0*\n\nA new alert event type has been added to allow Ziti components to emit alerts for issues that network operators can address.\nAlert events are generated when components encounter problems such as service configuration errors or resource\navailability issues.\n\nAlert events include:\n  - Alert source type and ID (currently supports routers, with controller and SDK support planned for future releases)\n  - Severity level (currently supports error, with info and warning planned for future releases)\n  - Alert message and supporting details\n  - Related entities (router, identity, service, etc.) associated with the alert\n\nExample alert event when a router cannot bind a configured network interface:\n\n```\n  {\n    \"namespace\": \"alert\",\n    \"event_src_id\": \"ctrl1\",\n    \"timestamp\": \"2021-11-08T14:45:45.785561479-05:00\",\n    \"alert_source_type\": \"router\",\n    \"alert_source_id\": \"DJFljCCoLs\",\n    \"severity\": \"error\",\n    \"message\": \"error starting proxy listener for service 'test'\",\n    \"details\": [\n      \"unable to bind eth0, no address\"\n    ],\n    \"related_entities\": {\n      \"router\": \"DJFljCCoLs\",\n      \"identity\": \"DJFljCCoLs\",\n      \"service\": \"3DPjxybDvXlo878CB0X2Zs\"\n    }\n  }\n```\n\nAlert events can be consumed through the standard event system and logged to configured event handlers for monitoring and alerting purposes.\n\nThese events are currently in Beta, as the format is still subject to change. Once they've been in use in production for a while\nand proven useful, they will be marked as stable.\n\n## Azure Service Bus Event Sink\n\n*Originally released in 1.7.0. Contributed by @ffaraone.*\n\nAdds support for streaming controller events to Azure Service Bus.\nThe new logger enables real-time event streaming from the OpenZiti controller to Azure Service Bus\nqueues or topics, providing integration with Azure-based monitoring and analytics systems.\n\nTo enable the Azure Service Bus event logger, add configuration to the controller config file under the events section:\n\n```\n  events:\n    serviceBusLogger:\n      subscriptions:\n        - type: circuit\n        - type: session\n        - type: metrics\n          sourceFilter: .*\n          metricFilter: .*\n        # Add other event types as needed\n      handler:\n        type: servicebus\n        format: json\n        connectionString: \"Endpoint=sb://your-namespace.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=your-key\"\n        topic: \"ziti-events\"          # Use 'topic' for Service Bus topic\n        # queue: \"ziti-events-queue\"  # Or use 'queue' for Service Bus queue\n        bufferSize: 100                # Optional, defaults to 50\n```\n\n- Required configuration:\n    - format: Event format, currently supports only json\n    - connectionString: Azure Service Bus connection string\n    - Either topic or queue: Destination name (mutually exclusive)\n\n- Optional configuration:\n    - bufferSize: Internal message buffer size (default: 50)\n\n\n## WWW-Authenticate Headers\n\nThe controller's Edge APIs now include `WWW-Authenticate` headers on `401 Unauthorized` responses,\nper issuer: https://github.com/openziti/ziti/issues/3356. These headers provide insight into why\na token was rejected. The main benefit of these headers is to convey information for JWT backed\nAPI Sessions and API Session authentication where a token may not have been provided (missing),\nis used beyond its expiration date (expired), or the token has become invalid for any other\nreason (invalid).\n\n`www-authenticate` headers are provided as a single header instance with multiple challenge values\nseparate by commas.\n\n### No Credentials Provided\n\nWhen a request hits a protected endpoint without any credentials, a single `WWW-Authenticate` header\nis returned listing both accepted auth schemes as comma-separated challenges:\n\n```\nWWW-Authenticate: zt-session realm=\"zt-session\" error=\"missing\" error_description=\"no matching token was provided\",Bearer realm=\"openziti-oidc\" error=\"missing\" error_description=\"no matching token was provided\"\n```\n\n### Token Errors\n\nWhen a token is present but cannot be accepted, the header identifies the scheme and what went wrong:\n\n```\nWWW-Authenticate: Bearer realm=\"openziti-oidc\" error=\"expired\" error_description=\"token expired\"\nWWW-Authenticate: Bearer realm=\"openziti-oidc\" error=\"invalid\" error_description=\"token is invalid\"\nWWW-Authenticate: zt-session realm=\"zt-session\" error=\"invalid\" error_description=\"token is invalid\"\n```\n\n### OIDC External JWT — Primary Authentication\n\nWhen an auth policy requires an external JWT signer for primary authentication (e.g., a PKCE flow\nbacked by an ext-jwt signer), the header identifies which signers are accepted and what went wrong.\nMultiple accepted signers are pipe-delimited in the `id` and `issuer` parameters:\n\n```\nWWW-Authenticate: Bearer realm=\"openziti-primary-ext-jwt\" error=\"missing\" error_description=\"no matching token was provided\" id=\"signer-id-1|signer-id-2\" issuer=\"https://issuer1.example.com|https://issuer2.example.com\"\n```\n\nThe `error` value follows the same `missing`/`expired`/`invalid` pattern as standard bearer token errors.\n\n### OIDC External JWT — Secondary / MFA Authentication\n\nWhen an auth policy requires an external JWT signer as a secondary factor (step-up after primary\nauth succeeds), the header identifies the single required signer. The `error` value follows the\nsame `missing`/`expired`/`invalid` pattern:\n\n```\nWWW-Authenticate: Bearer realm=\"openziti-secondary-ext-jwt\" error=\"missing\" error_description=\"no matching token was provided\" id=\"<signer-id>\" issuer=\"<issuer>\"\n```\n\n### Anonymous Endpoints\n\nUnauthenticated endpoints such as version information do not return `WWW-Authenticate` headers.\n\n## HA Preferred Leaders\n\nControllers can be marked as a preferred leader. \n\n**Example Config**\n```yaml\ncluster:\n  dataDir: /home/{{ .Model.MustVariable \"credentials.ssh.username\" }}/fablab/ctrldata\n  preferredLeader: true\n```\n\nIf a controller that is not marked preferredLeader becomes a preferredLeader, it will check \nif there's a node available that is marked as preferred. If there is one, or one later\njoins the cluster, the non-preferred node will attempt to transfer leadership to the \nnode that is marked as preferred.\n\n## Expanded Dynamic Cost Range\n\nThe dynamic cost range for smart routing has been expanded beyond the previous 64K limit. Under high\nload, terminators could saturate the cost space, making dynamic cost values meaningless for routing\ndecisions and leading to uneven load distribution. The expanded range allows for more granular cost\ndifferentiation even under heavy load.\n\n## Circuit ID and Error in Dial Failures\n\nDial failures now return the circuit ID and, when available, the error that caused the circuit to fail.\nPreviously, the circuit ID was only returned on successful dials. Note that SDKs will need to be\nupdated to surface the circuit id when a dial failure happens.\n\n## Multi-Underlay Control Channels\n\nRouter-to-controller control channels now support multiple underlays with priority-based message routing.\nThis allows time-sensitive control messages (heartbeats, routing, circuit requests) to be separated from\noperational data (metrics, inspections) across dedicated TCP connections, preventing bulk operations from\ndelaying user-affecting control plane traffic.\n\n## Dialing Identity Forwarded to Hosting SDK\n\nThe identity ID and name of the dialing client are now forwarded to the hosting SDK when a circuit is\nestablished. This allows hosting applications to identify which identity initiated the connection,\nenabling identity-aware request handling on the server side. This will require SDK updates to add this\nto the API for hosting applications.\n\n## Component Updates and Bug Fixes\n\n* github.com/openziti/channel/v4: [v4.2.41 -> v4.3.5](https://github.com/openziti/channel/compare/v4.2.41...v4.3.5)\n    * [Issue #228](https://github.com/openziti/channel/issues/228) - Ensure that Underlay never return nil on MultiChannel\n    * [Issue #226](https://github.com/openziti/channel/issues/226) - Allow specifying a minimum number of underlays for a channel, regardless of underlay type\n    * [Issue #225](https://github.com/openziti/channel/issues/225) - Add ChannelCreated to the UnderlayHandler API to allow handlers to be initialized with the channel before binding\n    * [Issue #224](https://github.com/openziti/channel/issues/224) - Update the underlay dispatcher to allow unknown underlay types to fall through to the default\n    * [Issue #222](https://github.com/openziti/channel/issues/222) - Allow injecting the underlay type into messages\n\n* github.com/openziti/edge-api: [v0.26.50 -> v0.26.56](https://github.com/openziti/edge-api/compare/v0.26.50...v0.26.56)\n    * [Issue #170](https://github.com/openziti/edge-api/issues/170) - Add preferredLeader flag to controllers\n    * [Issue #167](https://github.com/openziti/edge-api/issues/167) - Add ctrlChanListeners to router types\n    * [Issue #164](https://github.com/openziti/edge-api/issues/164) - Add permissions list to identity\n\n* github.com/openziti/foundation/v2: [v2.0.79 -> v2.0.87](https://github.com/openziti/foundation/compare/v2.0.79...v2.0.87)\n    * [Issue #464](https://github.com/openziti/foundation/issues/464) - Add support for -pre in versions\n\n* github.com/openziti/identity: [v1.0.118 -> v1.0.125](https://github.com/openziti/identity/compare/v1.0.118...v1.0.125)\n* github.com/openziti/metrics: [v1.4.2 -> v1.4.3](https://github.com/openziti/metrics/compare/v1.4.2...v1.4.3)\n    * [Issue #56](https://github.com/openziti/metrics/issues/56) - underlying resources of reference counted meters are not cleaned up when reference count hits zero\n\n* github.com/openziti/runzmd: [v1.0.84 -> v1.0.89](https://github.com/openziti/runzmd/compare/v1.0.84...v1.0.89)\n* github.com/openziti/sdk-golang: [v1.2.10 -> v1.4.2](https://github.com/openziti/sdk-golang/compare/v1.2.10...v1.4.2)\n    * [Issue #860](https://github.com/openziti/sdk-golang/issues/860) - Make the dialing identity's id and name available on dialed connections\n    * [Issue #857](https://github.com/openziti/sdk-golang/issues/857) - Use new error code and retry hints to correctly react to terminator errors\n    * [Issue #847](https://github.com/openziti/sdk-golang/issues/847) - Ensure the initial version check succeeds, to ensure we don't legacy sessions on ha or oidc-enabled controllers\n    * [Issue #824](https://github.com/openziti/sdk-golang/pull/824) - release notes and hard errors on no TOTP handler breaks partial auth events\n\n* github.com/openziti/secretstream: [v0.1.41 -> v0.1.47](https://github.com/openziti/secretstream/compare/v0.1.41...v0.1.47)\n* github.com/openziti/storage: [v0.4.31 -> v0.4.38](https://github.com/openziti/storage/compare/v0.4.31...v0.4.38)\n    * [Issue #122](https://github.com/openziti/storage/issues/122) - StringFuncNode has incorrect nil check, allowing panic\n    * [Issue #120](https://github.com/openziti/storage/issues/120) - Change post tx commit constraint handling order\n    * [Issue #119](https://github.com/openziti/storage/issues/119) - Add ContextDecorator API\n\n* github.com/openziti/transport/v2: [v2.0.198 -> v2.0.209](https://github.com/openziti/transport/compare/v2.0.198...v2.0.209)\n* github.com/openziti/xweb/v3: [v2.3.4 -> v3.0.3](https://github.com/openziti/xweb/compare/v2.3.4...v3.0.3)\n    * [Issue #32](https://github.com/openziti/xweb/issues/32) - watched identities sometimes don't reload when changed\n\n* github.com/openziti/ziti/v2: [v1.7.0 -> v2.0.0](https://github.com/openziti/ziti/compare/v1.7.0...v2.0.0)\n    * [Issue #3624](https://github.com/openziti/ziti/issues/3624) - Multi-underlay control channel doesn't correctly handle lack of group secret on non-grouped underlays\n    * [Issue #3613](https://github.com/openziti/ziti/issues/3613) - Initializing cluster from existing db using `db:` config settings results in panic\n    * [Issue #3620](https://github.com/openziti/ziti/issues/3620) - Controller control channel heartbeats config parsing was erroneously nested under options parsing\n    * [Issue #3619](https://github.com/openziti/ziti/issues/3619) - Router connect events full sync interval was using min/max values meant for the batch interval\n    * [Issue #3618](https://github.com/openziti/ziti/issues/3618) - Router interfaceDiscovery.minReportInterval value was being set to checkInterval\n    * [Issue #3617](https://github.com/openziti/ziti/issues/3617) - Transit router disabled flag not passed through raft command structure\n    * [Issue #3333](https://github.com/openziti/ziti/issues/3333) - Updb user-lockout triggered by successful login attempts\n    * [Issue #3599](https://github.com/openziti/ziti/issues/3599) - Add gap detection and handling to router data model\n    * [Issue #3356](https://github.com/openziti/ziti/issues/3356) - Add WWW-Authenticate Headers\n    * [Issue #3074](https://github.com/openziti/ziti/issues/3074) - Dynamic cost range is too limited\n    * [Issue #3558](https://github.com/openziti/ziti/issues/3558) - terminator cost increased on egress dial success, not on circuit completion\n    * [Issue #3556](https://github.com/openziti/ziti/issues/3556) - global circuit costs not cleared when terminator is deleted\n    * [Issue #3557](https://github.com/openziti/ziti/issues/3557) - costing calculation for the weighted terminator selection strategy  is incorrect\n    * [Issue #2512](https://github.com/openziti/ziti/issues/2512) - Return circuit ID and error in dial failures\n    * [Issue #3569](https://github.com/openziti/ziti/issues/3569) - Version 2.0+ routers should not connect to controllers which do not support JWT formatted legacy sessions\n    * [Issue #3565](https://github.com/openziti/ziti/issues/3565) - Link dialer save 'is first conn' true, so all dials claim to be first, causing potential race condition\n    * [Issue #3550](https://github.com/openziti/ziti/issues/3550) - Support multi-underlay control channels\n    * [Issue #3535](https://github.com/openziti/ziti/issues/3535) - Remove the legacy xgress_edge_tunnel implementation\n    * [Issue #3547](https://github.com/openziti/ziti/issues/3547) - Add support for sending the dialing identity id and name to the hosting sdk\n    * [Issue #3541](https://github.com/openziti/ziti/issues/3541) - Remove option to disable the router data model in the controller\n    * [Issue #3540](https://github.com/openziti/ziti/issues/3540) - Handle UDP difference between proxy and tproxy implementations\n    * [Issue #3527](https://github.com/openziti/ziti/issues/3527) - ER/T UDP tunnels keep closed connections for 30s, preventing potential new good connections in that time\n    * [Issue #3526](https://github.com/openziti/ziti/issues/3526) - ER/T half-close logic is incorrect\n    * [Issue #3524](https://github.com/openziti/ziti/issues/3524) - Provide more error context to SDKs for terminator errors\n    * [Issue #3509](https://github.com/openziti/ziti/issues/3509) - Enforce policy on the router for oidc sessions, by closing open circuits and terminators when service access is lost\n    * [Issue #3531](https://github.com/openziti/ziti/issues/3531) - Remove created/updated/deleted terminator events. Obsoleted by entity change events.\n    * [Issue #3532](https://github.com/openziti/ziti/issues/3532) - Removed deprecated create identity <type> subcommands\n    * [Issue #3521](https://github.com/openziti/ziti/issues/3521) - Cleanup CLI to remove calls to os.Exit to be embed friendlier\n    * [Issue #3516](https://github.com/openziti/ziti/issues/3516) - Remove support for create terminator v1\n    * [Issue #3512](https://github.com/openziti/ziti/issues/3512) - Remove legacy link management code from the controller\n    * [Issue #3511](https://github.com/openziti/ziti/issues/3511) - router proxy mode fails to resolve interface if binding is 0.0.0.0\n    * [Issue #3503](https://github.com/openziti/ziti/issues/3503) - Allow routers to request current cluster membership information\n    * [Issue #3501](https://github.com/openziti/ziti/issues/3501) - Get cluster membership information from raft directly, rather than trying to cache it in the DB\n    * [Issue #3500](https://github.com/openziti/ziti/issues/3500) - Set a router data model timeline when initializing a new HA setup, rather than letting it stay blank\n    * [Issue #3504](https://github.com/openziti/ziti/issues/3504) - Reduce router data model full state updates\n    * [Issue #3492](https://github.com/openziti/ziti/pull/3492) - Bump openziti/ziti-console-assets from 3.12.9 to 4.0.0 in /dist/docker-images/ziti-controller in the all group\n    * [Issue #3484](https://github.com/openziti/ziti/issues/3484) - router ctrl channel handler for handling cluster changes has an initialization race condition\n    * [Issue #3477](https://github.com/openziti/ziti/issues/3477) - Optionally enable model changes triggered by login to be non-blocking and to be droppable if the system is under load\n    * [Issue #3473](https://github.com/openziti/ziti/issues/3473) - Enable tls handshake rate limiter by default and tweak default values.\n    * [Issue #3471](https://github.com/openziti/ziti/issues/3471) - Go tunneler is ignoring host config MaxConnections\n    * [Issue #3469](https://github.com/openziti/ziti/issues/3469) - Only send model updates on resubscribe if the RDM index has advanced\n    * [Issue #2573](https://github.com/openziti/ziti/issues/2573) - An edge router in a tight restart loop causes a resource leak on routers to which it connects.\n    * [Issue #3430](https://github.com/openziti/ziti/issues/3430) - Add permissions list to identity\n    * [Issue #2109](https://github.com/openziti/ziti/issues/2109) - Add Edge Management Read Only Capability\n    * [Issue #3435](https://github.com/openziti/ziti/issues/3435) - Add edge management API permissions by entity type and action\n    * [Issue #3441](https://github.com/openziti/ziti/issues/3441) - Update router connection tracker to interrogate active connections\n    * [Issue #3451](https://github.com/openziti/ziti/issues/3451) - ci - compare only stable releases when promoting\n    * [Issue #3437](https://github.com/openziti/ziti/issues/3437) - SDK OIDC token updates to routers should return an error if invalid\n    * [Issue #3348](https://github.com/openziti/ziti/issues/3348) - Unable to clear/reset the \"tags\" property on an entity to an empty object\n    * [Issue #3452](https://github.com/openziti/ziti/issues/3452) - `ziti agent cluster add` has bad behavior if the add address doesn't match the advertise address\n    * [Issue #3410](https://github.com/openziti/ziti/issues/3410) - Consolidate fabric REST API code with edge management and edge client code\n    * [Issue #3425](https://github.com/openziti/ziti/issues/3425) - RDM not properly responding to tunneler enabled flag changes\n    * [Issue #3420](https://github.com/openziti/ziti/issues/3420) - The terminator id cache uses the same id for all terminators in a host.v2 config, resulting in a single terminator\n    * [Issue #3419](https://github.com/openziti/ziti/issues/3419) - When using the router data model, precedence specified on the per-service identity mapping are incorrectly interpreted\n    * [Issue #3318](https://github.com/openziti/ziti/issues/3318) - Terminator creation seems to slow exponentially as the number of terminators rises from 10k to 20k to 40k\n    * [Issue #3407](https://github.com/openziti/ziti/issues/3407) - The CLI doesn't properly pass JWT authentication information to websocket endpoints\n    * [Issue #3359](https://github.com/openziti/ziti/issues/3359) - Ensure router data model subscriptions have reasonable performance and will scale\n    * [Issue #3354](https://github.com/openziti/ziti/issues/3354) - SDK/ENV Info from SDKs is not distributed in HA\n    * [Issue #3381](https://github.com/openziti/ziti/issues/3381) - the fabric service REST apis are missing the maxIdleTime property\n    * [Issue #3382](https://github.com/openziti/ziti/issues/3382) - Legacy service sessions generated pre-1.7.x are incompatible with v1.7.+ and need to be cleared\n    * [Issue #3339](https://github.com/openziti/ziti/issues/3339) - get router ctrl.endpoint from ctrls claim in JWT\n    * [Issue #3378](https://github.com/openziti/ziti/issues/3378) - login with file stopped working\n    * [Issue #3349](https://github.com/openziti/ziti/issues/3349) - UPDB OIDC login returns wrong content type\n    * [Issue #2324](https://github.com/openziti/ziti/issues/2324) - Add Ext-JWT/OIDC enrollment\n    * [Issue #3346](https://github.com/openziti/ziti/issues/3346) - Fix confusing attempt logging\n    * [Issue #3337](https://github.com/openziti/ziti/issues/3337) - Router reports \"no xgress edge forwarder for circuit\"\n    * [Issue #3345](https://github.com/openziti/ziti/issues/3345) - Clean up connect events tests and remove global XG registry\n    * [Issue #3264](https://github.com/openziti/ziti/issues/3264) - Allow routers to generate alert events in cases of service misconfiguration\n\n","mentions_count":1},{"url":"https://api.github.com/repos/openziti/ziti/releases/291902379","assets_url":"https://api.github.com/repos/openziti/ziti/releases/291902379/assets","upload_url":"https://uploads.github.com/repos/openziti/ziti/releases/291902379/assets{?name,label}","html_url":"https://github.com/openziti/ziti/releases/tag/v2.0.0-fp1","id":291902379,"author":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"node_id":"RE_kwDODVFMN84RZhOr","tag_name":"v2.0.0-fp1","target_commitish":"main","name":"v2.0.0-fp1","draft":false,"immutable":false,"prerelease":true,"created_at":"2026-03-02T04:17:36Z","updated_at":"2026-03-02T04:35:08Z","published_at":"2026-03-02T04:29:39Z","assets":[{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/365043942","id":365043942,"node_id":"RA_kwDODVFMN84VwiDm","name":"checksums.sha256.txt","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"text/plain; charset=utf-8","state":"uploaded","size":782,"digest":"sha256:ec99237be77451a55162987d924d5b8dfbb670b2f3393c67daa2210cf19821ef","download_count":5,"created_at":"2026-03-02T04:29:37Z","updated_at":"2026-03-02T04:29:37Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-fp1/checksums.sha256.txt"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/365043940","id":365043940,"node_id":"RA_kwDODVFMN84VwiDk","name":"sbom-v2.0.0-fp1.spdx.json","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/json","state":"uploaded","size":996411,"digest":"sha256:5fbb9f9acb149140bbb4f6ba6b9c7338cc029546a8e22fa58239743d7e1f50dd","download_count":5,"created_at":"2026-03-02T04:29:37Z","updated_at":"2026-03-02T04:29:37Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-fp1/sbom-v2.0.0-fp1.spdx.json"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/365043937","id":365043937,"node_id":"RA_kwDODVFMN84VwiDh","name":"source-v2.0.0-fp1.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":3732935,"digest":"sha256:361a3df2b22b03229182482a819916b73114d0480bec2689495f4f3973c83219","download_count":7,"created_at":"2026-03-02T04:29:37Z","updated_at":"2026-03-02T04:29:37Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-fp1/source-v2.0.0-fp1.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/365043922","id":365043922,"node_id":"RA_kwDODVFMN84VwiDS","name":"ziti-darwin-amd64-2.0.0-fp1.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":54206760,"digest":"sha256:f413a1fb249f77ffde0947441422aa060164873a1c100a344aebced50cf47ecf","download_count":5,"created_at":"2026-03-02T04:29:35Z","updated_at":"2026-03-02T04:29:37Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-fp1/ziti-darwin-amd64-2.0.0-fp1.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/365043924","id":365043924,"node_id":"RA_kwDODVFMN84VwiDU","name":"ziti-darwin-arm64-2.0.0-fp1.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":50515249,"digest":"sha256:6508e6fb42392bff6e8f1acc5370969d668c5c838551016c63b8438d38171e8b","download_count":5,"created_at":"2026-03-02T04:29:35Z","updated_at":"2026-03-02T04:29:37Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-fp1/ziti-darwin-arm64-2.0.0-fp1.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/365043923","id":365043923,"node_id":"RA_kwDODVFMN84VwiDT","name":"ziti-linux-amd64-2.0.0-fp1.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":55522912,"digest":"sha256:c1155fdf74c9792b780ec0c1d741d3eb34fc68f1c348f9ae3dd6bd8aea2750a7","download_count":13,"created_at":"2026-03-02T04:29:35Z","updated_at":"2026-03-02T04:29:37Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-fp1/ziti-linux-amd64-2.0.0-fp1.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/365043921","id":365043921,"node_id":"RA_kwDODVFMN84VwiDR","name":"ziti-linux-arm-2.0.0-fp1.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":51942144,"digest":"sha256:580a909c3a7e26c89dcf7b3937d8fecee519fd1897db5872faa4b14ffc337821","download_count":7,"created_at":"2026-03-02T04:29:35Z","updated_at":"2026-03-02T04:29:37Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-fp1/ziti-linux-arm-2.0.0-fp1.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/365043925","id":365043925,"node_id":"RA_kwDODVFMN84VwiDV","name":"ziti-linux-arm64-2.0.0-fp1.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-gtar","state":"uploaded","size":52019862,"digest":"sha256:c4a889350183f0c3e10e655e3cf9a6c5680c2017b921ea10f8910c509cff924b","download_count":6,"created_at":"2026-03-02T04:29:35Z","updated_at":"2026-03-02T04:29:37Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-fp1/ziti-linux-arm64-2.0.0-fp1.tar.gz"},{"url":"https://api.github.com/repos/openziti/ziti/releases/assets/365043935","id":365043935,"node_id":"RA_kwDODVFMN84VwiDf","name":"ziti-windows-amd64-2.0.0-fp1.zip","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/zip","state":"uploaded","size":44899017,"digest":"sha256:08a42bc97b5ecd586817d47113a1690d1bf836fcee77540de83d7547c6f95209","download_count":8,"created_at":"2026-03-02T04:29:37Z","updated_at":"2026-03-02T04:29:38Z","browser_download_url":"https://github.com/openziti/ziti/releases/download/v2.0.0-fp1/ziti-windows-amd64-2.0.0-fp1.zip"}],"tarball_url":"https://api.github.com/repos/openziti/ziti/tarball/v2.0.0-fp1","zipball_url":"https://api.github.com/repos/openziti/ziti/zipball/v2.0.0-fp1","body":"# Release 2.0.0-fp1 (Feature Preview for Controller initiated control channel connections)\r\n\r\n**NOTE:** This is a special feature branch release, specifically for people interested in trying the controller-initiated control channels work. Others should try the -preN releases instead. This work should be included in a -preN release shortly.\r\n\r\n**Relevant release notes have been copied to the top**\r\n\r\n## Controller-Initiated Control Channel Dials (BETA)\r\n\r\nControllers can now dial routers to establish control channels. Previously, routers were solely\r\nresponsible for dialing controllers. This is useful in deployments where controllers are behind\r\nfirewalls and cannot be reached by all routers, but the controllers can reach the routers.\r\n\r\n### Router Configuration\r\n\r\nRouters can configure one or more control channel listeners. Each listener specifies a bind address,\r\nan advertise address (reported to the controller), and optional groups for matching.\r\n\r\n```yaml\r\nctrl:\r\n  listeners:\r\n    - bind: tls://0.0.0.0:6262\r\n      advertise: tls://router.example.com:6262\r\n      groups:\r\n        - default\r\n```\r\n\r\nThe advertise address is stored in the router's `ctrlChanListeners` model field and reported to\r\nthe controller. Groups default to `[\"default\"]` if not specified.\r\n\r\nRouters will also report their configured `ctrlChanListeners` to the controller when they connect,\r\nand the controller data model will be updated automatically.\r\n\r\nThe `ctrlChanListeners` field can also be set via the CLI:\r\n\r\n```bash\r\nziti edge update edge-router myRouter --ctrl-chan-listener 'tls://router.example.com:6262=group1,group2'\r\n```\r\n\r\n### Controller Configuration\r\n\r\nThe controller dialer is disabled by default and must be explicitly enabled. When enabled, the\r\ncontroller will dial routers that have control channel listeners configured and are not already\r\nconnected.\r\n\r\n```yaml\r\nctrl:\r\n  dialer:\r\n    enabled: true\r\n    groups:\r\n      - default\r\n    dialDelay: 30s\r\n```\r\n\r\n- `enabled` - Enables the controller dialer (default: `false`)\r\n- `groups` - List of groups to match against router listener groups (default: `[\"default\"]`)\r\n- `dialDelay` - Delay before the controller attempts to dial a disconnected router (default: `30s`)\r\n\r\nThe controller will only dial routers whose listener groups overlap with the controller's configured\r\ngroups.\r\n\r\n**2.0.0 Release Notes Below**\r\n-----------------------------------------------------------------\r\n \r\n## What's New\r\n\r\nThis is the next major version release of OpenZiti, following the 1.0 release in April 2024. \r\nOf particular note is that HA controllers are now considered ready for general use. \r\nThis release also introduces a new permissions model, OIDC/JWT token-based enrollment, \r\nclustering performance improvements, and a number of other features and fixes. Because \r\nsome of these changes are not backwards compatible with older routers, we're marking this \r\nas a major version bump.\r\n\r\n### HA Controllers are now considered ready for general use\r\n\r\nThis is a pretty big milestone and marks the completion of work that's been ongoing for a couple of years.\r\nThe HA work has brought with it some notable changes. Authentication now uses JWTs by default. Using JWTs\r\nmeans the controllers don't need to store session and propagate them to the routers. This removes a bottleneck\r\nfrom the network and allows the load to be more easily distributed among controllers and routers.\r\n\r\nTo support distributed authentication, the routers now get a bespoke version of the data model. In addition\r\nto enabling distributed authentication this will allow us to remove the need for service polling and further \r\nreduce the load on controllers in the future.\r\n\r\n### Router Compatibility\r\n\r\nRelated to the JWT work, routers with version 2.+ will only work with controllers that are version 2.+. This means\r\nto upgrade your network, controllers should be upgraded first. Routers can then be upgraded individually.\r\n\r\n2.x routers should still work fine with older router versions.\r\n\r\nWe try very hard to avoid breaking changes like this, but sometimes the engineering trade-offs lead there. This change\r\nwas first made in the 1.7 release. That release has not been marked stable, and we have no plans to do so, because\r\nof the backwards incompatibility.\r\n\r\n### New Permissions Model (BETA)\r\n\r\nAs one feature goes out of beta, another arrives into beta. This release introduces a new permissions system\r\nfor more fine grained control to the management API. It's not expected to change, but may do so based on feedback\r\nfrom users.\r\n\r\n### Updated Release Process\r\n\r\nWe have moved to creating `-preN` releases for major and minor versions. This way we can put out release candidates,\r\nor feature previews and put them through internal testing and let interested folks from the community try them out.\r\nThen, when we're ready, we can run the full validation suite against the last pre-release and retag it. \r\n\r\nPatch releases won't have `-preN` and should contain only high priority bug fixes.\r\n\r\n### Deprecation Cleanup\r\n\r\nSince we already have a breaking change, we're removing some other backwards compatibility code.\r\n\r\n* Controller managed links \r\n    * Router managed links were introduced in v0.30.0. \r\n    * If you're upgrading from an older versions, you'll want to upgrade to the latest 1.x release before jumping to 2.x\r\n    * Github tracking issue: https://github.com/openziti/ziti/issues/3512\r\n* `ziti edge create identity <type>`\r\n    * Identity types other than router were removed in v0.30.2\r\n    * The `type` can be dropped from the CLI command\r\n    * Github tracking issue: https://github.com/openziti/ziti/issues/3532\r\n* Terminator create/update/delete events\r\n    * These have been superseded by entity change events, which also have create/update/delete events for terminators\r\n    * Entity change events were introduced in v0.28.0\r\n    * Github tracking issue: https://github.com/openziti/ziti/issues/3531\r\n* `xgress_edge_tunnel` v1\r\n    * This is the first implementation of the tunneler in edge-router code (ER/T) which used legacy api sessions and services\r\n    * The v2 version uses the router data model and was introduced in v0.30.x\r\n    * Github tracking issue: https://github.com/openziti/ziti/issues/3516\r\n\r\n### Legacy Session Deprecation\r\n\r\nOIDC sessions are now preferred. They are the default, or will become the default for SDKs and tunnelers. They are also required\r\nwhen running HA. Legacy API and service session are now deprecated and will be removed in the OpenZiti v3.0.0 release. \r\n\r\n### Additional Features\r\n\r\n* Controllers can now optionally bind APIs using an OpenZiti identity\r\n* `ziti edge login` now supports the `--network-identity` flag to authenticate and establish connections through the Ziti overlay network\r\n* `ziti edge login` now supports using a bearer token with `--token` for authentication. The token is expected to be\r\n  provided as just the JWT, not with the \"Bearer \" prefix\r\n* Identity configuration can now be loaded from files or environment variables for flexible deployment scenarios\r\n* Identities can now be provisioned just-in-time through OIDC/JWT token-based enrollment\r\n* Multiple model updates can now be in-flight at the same time, improving clustering performance\r\n* Authentication-related model updates can now be non-blocking and even dropped if the system is too busy\r\n* Routers now provide more error context to SDKs for terminator errors, enabling better retry behavior\r\n* New `proxy.v1` config type for dynamic service proxies (originally released in 1.7.0)\r\n* New alert event type for surfacing operational issues to network operators - Beta (originally released in 1.7.0)\r\n* New Azure Service Bus event sink for streaming controller events, contributed by @ffaraone (originally released in 1.7.0)\r\n* Bundled ZAC upgraded to 4.0\r\n* Build updated to Go 1.25\r\n* CLI cleaned up to remove calls to `os.Exit`, making it more friendly for embedding\r\n* Controller Edge APIs now return `WWW-Authenticate` response headers on `401 Unauthorized` responses, giving clients actionable information about which auth methods are accepted and what went wrong\r\n* HA Controllers can be marked as 'preferredLeader' via config\r\n* Dynamic cost range for smart routing expanded beyond the previous 64K limit\r\n* Dial failures now return the circuit ID and error information for easier debugging\r\n* Router-to-controller control channels now support multiple underlays with priority-based message routing\r\n* The dialing identity's ID and name are now forwarded to the hosting SDK\r\n* Controllers can now dial routers to establish control channels, enabling connectivity when routers are behind firewalls (Beta)\r\n\r\n## Basic Permission System (BETA)\r\n\r\nAdded a basic permission system that allows more control over identity access to controller management API operations. \r\nThis replaces the previous binary admin/non-admin model with a more flexible permission system.\r\n\r\n**NOTE:** This feature is in BETA, primarily so we can get feedback on which permissions make sense. The implementation is unlikely to change\r\nbut the set of exposed permissions may grow, shrink or change based on user feedback. \r\n\r\n### Permission Model\r\n\r\nThe permission system supports three levels of authorization:\r\n\r\n  1. **Global Permissions**: System-wide access levels\r\n     - `admin` - Full access to all operations. This is still controlled by the `isAdmin` flag on identity\r\n     - `admin_readonly` - Read-only access to all resources except debugging facilities inspect and validate\r\n\r\n  2. **Entity-Level Permissions**: Full CRUD access to specific entity types\r\n     - Granting an entity-level permission (e.g., `service`) provides complete create, read, update, and delete access for that entity type\r\n\r\n  3. **Action-Level Permissions**: Specific operation access on entity types\r\n     - Fine-grained control using the pattern `<entity>.<action>` (e.g., `service.read`, `identity.update`)\r\n     - Supports `create`, `read`, `update`, and `delete` actions per entity type\r\n\r\n### Supported Entity Permissions\r\n\r\nThe following entity-level permissions are available:\r\n\r\n- `auth-policy` - Authentication policy management\r\n- `ca` - Certificate Authority management\r\n- `config` - Configuration management\r\n- `config-type` - Configuration type management\r\n- `edge-router-policy` - Edge router policy management\r\n- `enrollment` - Enrollment management\r\n- `external-jwt-signer` - External JWT signer management\r\n- `identity` - Identity management\r\n- `posture-check` - Posture check management\r\n- `router` - Edge and transit router management\r\n- `service` - Service management\r\n- `service-policy` - Service policy management\r\n- `service-edge-router-policy` - Service edge router policy management\r\n- `terminator` - Terminator management\r\n- `ops` - Operational resources (API sessions, sessions, circuits, links, inspect and validate)\r\n\r\n### Permission Assignment\r\n\r\nPermissions are assigned to identities via the `permissions` field in the identity resource. Multiple permissions can be granted to a single identity, and permissions are additive.\r\n\r\n### Cross-Entity Operations\r\n\r\nListing related entities through an entity's endpoints requires appropriate permissions for the related entity type. For example:\r\n- Listing services for a service-policy requires `service.read` permission\r\n- Listing identities for an edge-router-policy requires `identity.read` permission\r\n- Listing configs for a service requires `config.read` permission\r\n\r\n**NOTE:** \r\nMore permissions than expected may be required when performing actions through the CLI or ZAC. Take for example, when an identity \r\nhas `config.create` and is attempting to create a new config. The CLI may fail if the identity doesn't have `config-type.read`\r\nas well because it will need to look up the config type id that corresponds to the given config type name.\r\n\r\nSimilar cross entity read permissions may be required when creating services.\r\n\r\n### Admin Protection\r\n\r\nNon-admin identities cannot:\r\n- Create identities with the `isAdmin` flag\r\n- Create identities with any permissions granted\r\n- Modify admin-related fields on existing identities\r\n- Update or delete admin identities\r\n- Grant permissions to identities\r\n\r\nThese protections ensure that privilege escalation is prevented and admin access remains controlled.\r\n\r\n\r\n## Binding Controller APIs With Identity\r\n\r\nController APIs can now be bound to an OpenZiti overlay network identity, allowing secure communication through\r\nthe Ziti network. This is useful for scenarios where you want to expose controller APIs only through the overlay\r\nnetwork rather than on a standard network interface.\r\n\r\n### Configuration Structure\r\n\r\nA standard `bindPoint` configuration looks like this:\r\n```text\r\n    bindPoints:\r\n      - interface: 127.0.0.1:18441\r\n        address: 127.0.0.1:18441\r\n```\r\n\r\nTo bind controller APIs to an OpenZiti identity, add an additional `identity` block to your `bindPoints`. The\r\nidentity configuration specifies where to load the Ziti identity file and which service to bind it to:\r\n\r\n```text\r\n    bindPoints:\r\n      - interface: 127.0.0.1:18441\r\n        address: 127.0.0.1:18441\r\n      - identity:\r\n          file: \"c:/temp/ctrl.testing/ctrl.identity.json\"\r\n          service: \"mgmt\"\r\n```\r\n\r\n### Supported Configuration Options\r\n\r\n- `file`: Path to a Ziti identity JSON file containing the controller's identity and enrollment certificate\r\n- `env`: Name of an environment variable containing a base64-encoded Ziti identity (alternative to `file`)\r\n- `service`: The name of the Ziti service to bind the controller API to\r\n\r\n### Using Environment Variables\r\n\r\nFor deployments where storing identity files on disk is not preferred, you can reference a base64-encoded\r\nidentity file from an environment variable. The environment variable should contain the base64-encoded contents\r\nof the identity JSON file.\r\n\r\nFor example, if an environment variable named `ZITI_CTRL_IDENTITY` contains a base64-encoded identity file:\r\n\r\n```text\r\n    bindPoints:\r\n      - interface: 127.0.0.1:18441\r\n        address: 127.0.0.1:18441\r\n      - identity:\r\n          env: ZITI_CTRL_IDENTITY\r\n          service: \"mgmt\"\r\n```\r\n\r\n### IPv6 Support\r\n\r\nBoth IPv4 and IPv6 addresses are supported for standard bind points. IPv6 addresses should be specified in bracket\r\nnotation with a port number:\r\n\r\n```text\r\n    bindPoints:\r\n      - interface: \"[::1]:18441\"\r\n        address: \"[::1]:18441\"\r\n      - identity:\r\n          file: \"/path/to/identity.json\"\r\n          service: \"mgmt\"\r\n```\r\n\r\n## CLI Enhancements for Identity-Based Connections\r\n\r\nThe `ziti edge login` command and REST client utilities have been enhanced to support identity-based connections\r\nthrough the Ziti overlay network.\r\n\r\n### New `--network-identity` Flag for `ziti edge login`\r\n\r\nThe `ziti edge login` command now includes a `--network-identity` flag that allows you to authenticate to a Ziti\r\ncontroller through the overlay network using a Ziti identity:\r\n\r\n```bash\r\nziti edge login https://ziti.mgmt.apis.local:1280 \\\r\n  --username myuser \\\r\n  --password mypass \\\r\n  --network-identity /path/to/identity.json\r\n```\r\n\r\nThis is useful when the controller is only accessible through the Ziti overlay network or when you want to ensure\r\nall communication to the controller flows through the overlay for security purposes.\r\n\r\n### Identity Resolution Order\r\n\r\nWhen establishing connections, identities are resolved in the following order:\r\n\r\n1. **Command-line flag**: The `--network-identity` flag takes precedence\r\n2. **Environment variable**: If `ZITI_CLI_NETWORK_ID` is set and contains a base64-encoded identity, it is used\r\n3. **Cached identity file**: If a network identity was saved from a previous login in the Ziti config directory, it may be used\r\n\r\nThis layered approach allows for flexibility in deployment scenarios:\r\n- Development: Use command-line flags for quick testing\r\n- Automation: Use environment variables in CI/CD pipelines\r\n- Production: Cache identities securely for repeated access\r\n\r\n#### Dialing Modes When Authenticating\r\n\r\nThe CLI supports two dialing modes:\r\n\r\n**Intercept-based Dialing (Default)**\r\nBy default, URLs are expected to leverage intercepts. Create a service with an appropriate intercept config and use\r\nthe intercept address when dialing. This is the standard mode for most use cases. For example, given a service with\r\nthe intercept `ziti.mgmt.apis.local`\r\n```bash\r\nziti edge login https://ziti.mgmt.apis.local:1280 \\\r\n  --username myuser \\\r\n  --password mypass \\\r\n  --network-identity /path/to/identity.json\r\n```\r\n\r\n**Identity-aware Dialing (Addressable Terminators)**\r\nTo support addressable terminators-based dialing, specify a user in the URL. This activates dial-by-identity\r\nfunctionality. The URL format should be `identity-to-dial@service-name-to-dial`. For example:\r\n```bash\r\nziti edge login https://my-identity@my-service:1280 \\\r\n  --username myuser \\\r\n  --password mypass \\\r\n  --network-identity /path/to/identity.json\r\n```\r\n\r\nIn this mode, the transport extracts the identity from the URL and uses it to establish a direct connection to\r\nthe specified service via the addressable terminator.\r\n\r\n\r\n## OIDC/JWT Token-based Enrollment\r\n\r\nOpenZiti now supports provisioning identities just-in-time through OIDC/JWT token enrollment. External identity \r\nproviders can be configured to allow identities to enroll using JWT tokens, with support for the resulting \r\nidentities to use certificate or token authentication.\r\n\r\n### External JWT Signer Configuration\r\n\r\nExternal JWT signers are configured via the Edge Management API to define enrollment behavior with the following new \r\nenrollment-specific properties:\r\n\r\n- **enrollToCertEnabled** - When enabled, identities can exchange a JWT token and a certificate signing request (CSR) \r\n        for a client certificate during enrollment. The certificate can then be used for standard certificate-based\r\n        authentication.\r\n\r\n- **enrollToTokenEnabled** - When enabled, identities can use a JWT token to enroll. The current token or future tokens\r\n        may be used for authentication.\r\n\r\n- **enrollNameClaimsSelector** - Specifies which JWT claim contains the identity name. Accepts a JSON pointer \r\n        (e.g., `/preferred_username`) or a simple property name (e.g., `preferred_username`, automatically converted to\r\n        `/preferred_username`). Defaults to `/sub` if not specified. The extracted value becomes the identity name in Ziti.\r\n\r\n- **enrollAttributeClaimsSelector** - Specifies which JWT claims to extract as identity attributes during enrollment.\r\n        Accepts a JSON pointer (e.g., `/roles`) or a simple property name (e.g., `roles`). Extracted attributes are \r\n        applied to the newly enrolled identity for use in authorization policies.\r\n\r\n- **enrollAuthPolicyId** - Specifies the authentication policy to apply to newly enrolled identities. This determines\r\n        what authentication methods are available for the identity post-enrollment.\r\n\r\nAdditionally the existing property named **claimsProperty** that specifies external id to match identities to:\r\n\r\n- now supports a JSON pointer (e.g., `/id`) or a simple property name (e.g., `id`)\r\n- is used to populate the `externalId` field of the identity\r\n\r\n### Enrollment Paths\r\n\r\n#### Certificate Enrollment (enrollToCertEnabled)\r\n\r\nWhen certificate enrollment is enabled, unauthenticated users can:\r\n\r\n1. Obtain a list of available IdPs from the public Edge Client API `GET /external-jwt-signers` endpoint, where \r\n   `enrollToCertEnabled` is set to `true`\r\n2. Obtain a JWT from the configured OIDC provider\r\n3. Generate a certificate signing request (CSR)\r\n4. Submit an enrollment request with the JWT and CSR\r\n5. Have their identity created in Ziti with attributes extracted from JWT claims\r\n6. Receive a signed client certificate for certificate-based authentication\r\n\r\n#### Token Enrollment (enrollToTokenEnabled)\r\n\r\nWhen token enrollment is enabled, unauthenticated users can:\r\n\r\n1. Obtain a list of available IdPs from the public Edge Client API `GET /external-jwt-signers` endpoint, where \r\n   `enrollToTokenEnabled` is set to `true`\r\n1. Obtain a JWT from the configured OIDC provider\r\n2. Submit an enrollment request with the JWT\r\n3. Have their identity created in Ziti with attributes extracted from JWT claims\r\n4. Receive a Ziti API token for token-based authentication\r\n\r\n### Edge Management API\r\n\r\nThe Edge Management API provides full CRUD operations for configuring external JWT signers:\r\n\r\n- `POST /external-jwt-signers` - Create a new external JWT signer with all configuration options\r\n- `GET /external-jwt-signers` - List all configured external JWT signers\r\n- `GET /external-jwt-signers/{id}` - Retrieve a specific signer configuration\r\n- `PUT /external-jwt-signers/{id}` - Update all fields of a signer\r\n- `PATCH /external-jwt-signers/{id}` - Partially update a signer\r\n- `DELETE /external-jwt-signers/{id}` - Delete a signer\r\n\r\n### Edge Client API\r\n\r\nThe Edge Client API exposes a reduced set of external JWT signer information for unauthenticated enrollment requests:\r\n\r\n- `GET /external-jwt-signers` - List available JWT signers with enrollment capabilities\r\n\r\nThe client API response includes the following fields for each signer:\r\n\r\n- `name` - Signer name\r\n- `externalAuthUrl` - URL where users obtain JWT tokens\r\n- `clientId` - OIDC client ID\r\n- `scopes` - Requested OIDC scopes\r\n- `openIdConfigurationUrl` - OIDC discovery endpoint\r\n- `audience` - Expected token audience\r\n- `targetToken` - Token type to use (ACCESS or ID)\r\n- **`enrollToCertEnabled`** - Flag indicating certificate enrollment is available\r\n- **`enrollToTokenEnabled`** - Flag indicating token enrollment is available\r\n\r\n### CLI Commands\r\n\r\n**Create an external JWT signer with enrollment options:**\r\n```\r\nziti edge controller create ext-jwt-signer <name> <issuer> \\\r\n  --jwks-endpoint <url> \\\r\n  --audience <audience> \\\r\n  --enroll-to-cert \\\r\n  --enroll-to-token=false \\\r\n  --enroll-name-claims-selector preferred_username \\\r\n  --enroll-attr-claims-selector roles \\\r\n  --enroll-auth-policy <policy-id-or-name>\r\n```\r\n\r\n**Update enrollment options on an existing signer:**\r\n```\r\nziti edge controller update ext-jwt-signer <name|id> \\\r\n  --enroll-to-cert \\\r\n  --enroll-auth-policy <policy-id-or-name>\r\n```\r\n\r\n**List external JWT signers:**\r\n```\r\nziti edge controller list ext-jwt-signers\r\n```\r\n\r\n## Clustering Performance Improvements\r\n\r\nIn previous releases, model updates were submitted to raft one at at time. This prevented \r\nraft from being efficient by allowing command batching. This release allows multiple \r\nmodel updates to be in-flight at the same time. \r\n\r\nNew Configuration Options\r\n\r\n1. Raft Apply Timeout (raft.applyTimeout)\r\n\r\nLocation: Controller configuration file, under raft section\r\nType: Duration\r\nDefault: 5s\r\nDescription: Timeout for applying commands to the Raft distributed log. Commands that exceed this timeout will trigger adaptive rate limiter backoff.\r\n\r\nExample:\r\n```\r\n  raft:\r\n    applyTimeout: 10s\r\n```\r\n\r\n2. Raft Rate Limiter Configuration (raft.rateLimiter)\r\n\r\nA new adaptive rate limiter that controls the submission of commands to the Raft cluster. Unlike the existing command rate limiter, this specifically manages in-flight Raft operations with adaptive window sizing.\r\n\r\nConfiguration Structure:\r\n```\r\n  raft:\r\n    rateLimiter:\r\n      enabled: true\r\n      minSize: 5\r\n      maxSize: 250\r\n      timeout: 30s\r\n```\r\n\r\nSub-options:\r\n\r\n  - enabled (boolean)\r\n    - Default: true\r\n    - Description: Enable/disable adaptive rate limiting for Raft command submission\r\n  - minSize (integer)\r\n    - Default: 5\r\n    - Minimum: 1\r\n    - Description: Minimum window size for concurrent in-flight Raft operations\r\n  - maxSize (integer)\r\n    - Default: 250\r\n    - Description: Maximum window size for concurrent in-flight Raft operations. Must be >= minSize\r\n  - timeout (duration)\r\n    - Default: 30s\r\n    - Description: Time after which outstanding work is assumed to have failed if not marked completed\r\n\r\n3. Restart Self on Snapshot (raft.restartSelfOnSnapshot)\r\n\r\nLocation: Controller configuration file, under raft section\r\nType: Boolean\r\nDefault: false\r\nDescription: When true, the controller will automatically restart itself when restoring a snapshot to an initialized system. When false, the controller will exit with code 0, requiring external process management to restart it.\r\n\r\nExample:\r\n```\r\n  raft:\r\n    restartSelfOnSnapshot: true\r\n```\r\n\r\n### New Metrics\r\n\r\nThe adaptive rate limiter exposes three new metrics:\r\n\r\n  1. raft.rate_limiter.queue_size (gauge)\r\n    - Current number of operations queued/in-flight\r\n  2. raft.rate_limiter.work_timer (timer)\r\n    - Duration of rate-limited operations\r\n  3. raft.rate_limiter.window_size (gauge)\r\n    - Current adaptive window size\r\n\r\n## Background Processing for Identity Updates\r\n\r\nIdentity environment and authenticator updates that occur during authentication are now processed asynchronously in the background. \r\nThis prevents authentication requests from blocking when the system is under load, significantly improving resilience during thundering herd scenarios.\r\n\r\nWhen the background queue fills up, updates can be dropped as they will be refreshed on the next authentication attempt. \r\nThis allows the system to gracefully handle load spikes without impacting authentication performance.\r\n\r\nFor now, dropping entries when the queue fills will be disabled by default, but can be enabled, see below.\r\n\r\n### Configuration\r\n\r\nA new `command.background` configuration section controls the background processing behavior:\r\n\r\n```yaml\r\n  command:\r\n    background:\r\n      enabled: true           # Enable background processing (default: true)\r\n      queueSize: 1000        # Maximum queue size (default: 1000)\r\n      dropWhenFull: true     # Drop updates when queue is full (default: false)\r\n      delayThreshold: 50ms   # The threshold for how long updates are taking before starting to background updates\r\n```\r\n\r\nNote that the `commandRateLimiter` configuration section may instead be specified under `command` as `rateLimiter`.\r\n\r\nExample:\r\n\r\n```yaml\r\n  command:\r\n    background:\r\n      enabled: true\r\n      queueSize: 250\r\n      dropWhenFull: false\r\n      delayThreshold: 50ms\r\n    rateLimiter:\r\n      enabled:   true\r\n      maxQueued: 25\r\n```\r\n\r\nNote that if command rate limiter configuration is specified in both locations, the settings under `command` will take \r\nprecedence. The standalone `commandRateLimiter` section may be deprecated in the future.\r\n\r\n### Metrics\r\n\r\nWhen background processing is enabled, the following metrics are exposed:\r\n\r\n- command.background.queue_size - Current number of queued background tasks\r\n- command.background.worker_count - Current number of worker goroutines\r\n- command.background.busy_workers - Number of workers currently processing tasks\r\n- command.background.work_timer - Timer tracking background task execution (includes histogram, meter, and count)\r\n- command.background.dropped_entries - Count of dropped updates when queue is full (only when dropWhenFull is enabled)\r\n\r\n## New proxy.v1 Config Type\r\n\r\n*Originally released in 1.7.0*\r\n\r\nAdded support for dynamic service proxies with configurable binding and protocol options.\r\nThis allows Edge Routers and Tunnelers to create proxy endpoints that can forward traffic for Ziti services.\r\n\r\nThis differs from intercept.v1 in that intercept.v1 will intercept traffic on specified\r\nIP addresses or DNS entries to forward to a service using tproxy or tun interface,\r\ndepending on implementation.\r\n\r\nA proxy on the other hand will just start a regular TCP/UDP listener on the configured port,\r\nso traffic will have to be configured for that destination.\r\n\r\nExample proxy.v1 Configuration:\r\n\r\n```\r\n  {\r\n    \"port\": 8080,\r\n    \"protocols\": [\"tcp\"],\r\n    \"binding\": \"0.0.0.0\"\r\n  }\r\n```\r\n\r\nConfiguration Properties:\r\n  - port (required): Port number to listen on (1-65535)\r\n  - protocols (required): Array of supported protocols (tcp, udp)\r\n  - binding (optional): Interface to bind to. For the ER/T defaults to the configured lanIF config property.\r\n\r\nThis config type is currently supported by the ER/T when running in either proxy or tproxy mode.\r\n\r\n## Alert Events (BETA)\r\n\r\n*Originally released in 1.7.0*\r\n\r\nA new alert event type has been added to allow Ziti components to emit alerts for issues that network operators can address.\r\nAlert events are generated when components encounter problems such as service configuration errors or resource\r\navailability issues.\r\n\r\nAlert events include:\r\n  - Alert source type and ID (currently supports routers, with controller and SDK support planned for future releases)\r\n  - Severity level (currently supports error, with info and warning planned for future releases)\r\n  - Alert message and supporting details\r\n  - Related entities (router, identity, service, etc.) associated with the alert\r\n\r\nExample alert event when a router cannot bind a configured network interface:\r\n\r\n```\r\n  {\r\n    \"namespace\": \"alert\",\r\n    \"event_src_id\": \"ctrl1\",\r\n    \"timestamp\": \"2021-11-08T14:45:45.785561479-05:00\",\r\n    \"alert_source_type\": \"router\",\r\n    \"alert_source_id\": \"DJFljCCoLs\",\r\n    \"severity\": \"error\",\r\n    \"message\": \"error starting proxy listener for service 'test'\",\r\n    \"details\": [\r\n      \"unable to bind eth0, no address\"\r\n    ],\r\n    \"related_entities\": {\r\n      \"router\": \"DJFljCCoLs\",\r\n      \"identity\": \"DJFljCCoLs\",\r\n      \"service\": \"3DPjxybDvXlo878CB0X2Zs\"\r\n    }\r\n  }\r\n```\r\n\r\nAlert events can be consumed through the standard event system and logged to configured event handlers for monitoring and alerting purposes.\r\n\r\nThese events are currently in Beta, as the format is still subject to change. Once they've been in use in production for a while\r\nand proven useful, they will be marked as stable.\r\n\r\n## Azure Service Bus Event Sink\r\n\r\n*Originally released in 1.7.0. Contributed by @ffaraone.*\r\n\r\nAdds support for streaming controller events to Azure Service Bus.\r\nThe new logger enables real-time event streaming from the OpenZiti controller to Azure Service Bus\r\nqueues or topics, providing integration with Azure-based monitoring and analytics systems.\r\n\r\nTo enable the Azure Service Bus event logger, add configuration to the controller config file under the events section:\r\n\r\n```\r\n  events:\r\n    serviceBusLogger:\r\n      subscriptions:\r\n        - type: circuit\r\n        - type: session\r\n        - type: metrics\r\n          sourceFilter: .*\r\n          metricFilter: .*\r\n        # Add other event types as needed\r\n      handler:\r\n        type: servicebus\r\n        format: json\r\n        connectionString: \"Endpoint=sb://your-namespace.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=your-key\"\r\n        topic: \"ziti-events\"          # Use 'topic' for Service Bus topic\r\n        # queue: \"ziti-events-queue\"  # Or use 'queue' for Service Bus queue\r\n        bufferSize: 100                # Optional, defaults to 50\r\n```\r\n\r\n- Required configuration:\r\n    - format: Event format, currently supports only json\r\n    - connectionString: Azure Service Bus connection string\r\n    - Either topic or queue: Destination name (mutually exclusive)\r\n\r\n- Optional configuration:\r\n    - bufferSize: Internal message buffer size (default: 50)\r\n\r\n\r\n## WWW-Authenticate Headers\r\n\r\nThe controller's Edge APIs now include `WWW-Authenticate` headers on `401 Unauthorized` responses,\r\nper issuer: https://github.com/openziti/ziti/issues/3356. These headers provide insight into why\r\na token was rejected. The main benefit of these headers is to convey information for JWT backed\r\nAPI Sessions and API Session authentication where a token may not have been provided (missing),\r\nis used beyond its expiration date (expired), or the token has become invalid for any other\r\nreason (invalid).\r\n\r\n`www-authenticate` headers are provided as a single header instance with multiple challenge values\r\nseparate by commas.\r\n\r\n### No Credentials Provided\r\n\r\nWhen a request hits a protected endpoint without any credentials, a single `WWW-Authenticate` header\r\nis returned listing both accepted auth schemes as comma-separated challenges:\r\n\r\n```\r\nWWW-Authenticate: zt-session realm=\"zt-session\" error=\"missing\" error_description=\"no matching token was provided\",Bearer realm=\"openziti-oidc\" error=\"missing\" error_description=\"no matching token was provided\"\r\n```\r\n\r\n### Token Errors\r\n\r\nWhen a token is present but cannot be accepted, the header identifies the scheme and what went wrong:\r\n\r\n```\r\nWWW-Authenticate: Bearer realm=\"openziti-oidc\" error=\"expired\" error_description=\"token expired\"\r\nWWW-Authenticate: Bearer realm=\"openziti-oidc\" error=\"invalid\" error_description=\"token is invalid\"\r\nWWW-Authenticate: zt-session realm=\"zt-session\" error=\"invalid\" error_description=\"token is invalid\"\r\n```\r\n\r\n### OIDC External JWT — Primary Authentication\r\n\r\nWhen an auth policy requires an external JWT signer for primary authentication (e.g., a PKCE flow\r\nbacked by an ext-jwt signer), the header identifies which signers are accepted and what went wrong.\r\nMultiple accepted signers are pipe-delimited in the `id` and `issuer` parameters:\r\n\r\n```\r\nWWW-Authenticate: Bearer realm=\"openziti-primary-ext-jwt\" error=\"missing\" error_description=\"no matching token was provided\" id=\"signer-id-1|signer-id-2\" issuer=\"https://issuer1.example.com|https://issuer2.example.com\"\r\n```\r\n\r\nThe `error` value follows the same `missing`/`expired`/`invalid` pattern as standard bearer token errors.\r\n\r\n### OIDC External JWT — Secondary / MFA Authentication\r\n\r\nWhen an auth policy requires an external JWT signer as a secondary factor (step-up after primary\r\nauth succeeds), the header identifies the single required signer. The `error` value follows the\r\nsame `missing`/`expired`/`invalid` pattern:\r\n\r\n```\r\nWWW-Authenticate: Bearer realm=\"openziti-secondary-ext-jwt\" error=\"missing\" error_description=\"no matching token was provided\" id=\"<signer-id>\" issuer=\"<issuer>\"\r\n```\r\n\r\n### Anonymous Endpoints\r\n\r\nUnauthenticated endpoints such as version information do not return `WWW-Authenticate` headers.\r\n\r\n## HA Preferred Leaders\r\n\r\nControllers can be marked as a preferred leader. \r\n\r\n**Example Config**\r\n```yaml\r\ncluster:\r\n  dataDir: /home/{{ .Model.MustVariable \"credentials.ssh.username\" }}/fablab/ctrldata\r\n  preferredLeader: true\r\n```\r\n\r\nIf a controller that is not marked preferredLeader becomes a preferredLeader, it will check \r\nif there's a node available that is marked as preferred. If there is one, or one later\r\njoins the cluster, the non-preferred node will attempt to transfer leadership to the \r\nnode that is marked as preferred.\r\n\r\n## Expanded Dynamic Cost Range\r\n\r\nThe dynamic cost range for smart routing has been expanded beyond the previous 64K limit. Under high\r\nload, terminators could saturate the cost space, making dynamic cost values meaningless for routing\r\ndecisions and leading to uneven load distribution. The expanded range allows for more granular cost\r\ndifferentiation even under heavy load.\r\n\r\n## Circuit ID and Error in Dial Failures\r\n\r\nDial failures now return the circuit ID and, when available, the error that caused the circuit to fail.\r\nPreviously, the circuit ID was only returned on successful dials. Note that SDKs will need to be\r\nupdated to surface the circuit id when a dial failure happens.\r\n\r\n## Multi-Underlay Control Channels\r\n\r\nRouter-to-controller control channels now support multiple underlays with priority-based message routing.\r\nThis allows time-sensitive control messages (heartbeats, routing, circuit requests) to be separated from\r\noperational data (metrics, inspections) across dedicated TCP connections, preventing bulk operations from\r\ndelaying user-affecting control plane traffic.\r\n\r\n## Dialing Identity Forwarded to Hosting SDK\r\n\r\nThe identity ID and name of the dialing client are now forwarded to the hosting SDK when a circuit is\r\nestablished. This allows hosting applications to identify which identity initiated the connection,\r\nenabling identity-aware request handling on the server side. This will require SDK updates to add this\r\nto the API for hosting applications.\r\n\r\n## Controller-Initiated Control Channel Dials (BETA)\r\n\r\nControllers can now dial routers to establish control channels. Previously, routers were solely\r\nresponsible for dialing controllers. This is useful in deployments where controllers are behind\r\nfirewalls and cannot be reached by all routers, but the controllers can reach the routers.\r\n\r\n### Router Configuration\r\n\r\nRouters can configure one or more control channel listeners. Each listener specifies a bind address,\r\nan advertise address (reported to the controller), and optional groups for matching.\r\n\r\n```yaml\r\nctrl:\r\n  listeners:\r\n    - bind: tls://0.0.0.0:6262\r\n      advertise: tls://router.example.com:6262\r\n      groups:\r\n        - default\r\n```\r\n\r\nThe advertise address is stored in the router's `ctrlChanListeners` model field and reported to\r\nthe controller. Groups default to `[\"default\"]` if not specified.\r\n\r\nRouters will also report their configured `ctrlChanListeners` to the controller when they connect,\r\nand the controller data model will be updated automatically.\r\n\r\nThe `ctrlChanListeners` field can also be set via the CLI:\r\n\r\n```bash\r\nziti edge update edge-router myRouter --ctrl-chan-listener 'tls://router.example.com:6262=group1,group2'\r\n```\r\n\r\n### Controller Configuration\r\n\r\nThe controller dialer is disabled by default and must be explicitly enabled. When enabled, the\r\ncontroller will dial routers that have control channel listeners configured and are not already\r\nconnected.\r\n\r\n```yaml\r\nctrl:\r\n  dialer:\r\n    enabled: true\r\n    groups:\r\n      - default\r\n    dialDelay: 30s\r\n```\r\n\r\n- `enabled` - Enables the controller dialer (default: `false`)\r\n- `groups` - List of groups to match against router listener groups (default: `[\"default\"]`)\r\n- `dialDelay` - Delay before the controller attempts to dial a disconnected router (default: `30s`)\r\n\r\nThe controller will only dial routers whose listener groups overlap with the controller's configured\r\ngroups.\r\n\r\n## Current Beta Features\r\n\r\n* Basic Permission System\r\n* Alert Events\r\n* Controller-Initiated Control Channel Dials\r\n\r\n## Component Updates and Bug Fixes\r\n\r\n* github.com/openziti/channel/v4: [v4.2.41 -> v4.3.5](https://github.com/openziti/channel/compare/v4.2.41...v4.3.5)\r\n    * [Issue #228](https://github.com/openziti/channel/issues/228) - Ensure that Underlay never return nil on MultiChannel\r\n    * [Issue #226](https://github.com/openziti/channel/issues/226) - Allow specifying a minimum number of underlays for a channel, regardless of underlay type\r\n    * [Issue #225](https://github.com/openziti/channel/issues/225) - Add ChannelCreated to the UnderlayHandler API to allow handlers to be initialized with the channel before binding\r\n    * [Issue #224](https://github.com/openziti/channel/issues/224) - Update the underlay dispatcher to allow unknown underlay types to fall through to the default\r\n    * [Issue #222](https://github.com/openziti/channel/issues/222) - Allow injecting the underlay type into messages\r\n\r\n* github.com/openziti/edge-api: [v0.26.50 -> v0.26.53](https://github.com/openziti/edge-api/compare/v0.26.50...v0.26.53)\r\n    * [Issue #164](https://github.com/openziti/edge-api/issues/164) - Add permissions list to identity\r\n\r\n* github.com/openziti/foundation/v2: [v2.0.79 -> v2.0.87](https://github.com/openziti/foundation/compare/v2.0.79...v2.0.87)\r\n    * [Issue #464](https://github.com/openziti/foundation/issues/464) - Add support for -pre in versions\r\n\r\n* github.com/openziti/identity: [v1.0.118 -> v1.0.125](https://github.com/openziti/identity/compare/v1.0.118...v1.0.125)\r\n* github.com/openziti/metrics: [v1.4.2 -> v1.4.3](https://github.com/openziti/metrics/compare/v1.4.2...v1.4.3)\r\n    * [Issue #56](https://github.com/openziti/metrics/issues/56) - underlying resources of reference counted meters are not cleaned up when reference count hits zero\r\n\r\n* github.com/openziti/runzmd: [v1.0.84 -> v1.0.89](https://github.com/openziti/runzmd/compare/v1.0.84...v1.0.89)\r\n* github.com/openziti/sdk-golang: [v1.2.10 -> v1.4.2](https://github.com/openziti/sdk-golang/compare/v1.2.10...v1.4.2)\r\n    * [Issue #860](https://github.com/openziti/sdk-golang/issues/860) - Make the dialing identity's id and name available on dialed connections\r\n    * [Issue #857](https://github.com/openziti/sdk-golang/issues/857) - Use new error code and retry hints to correctly react to terminator errors\r\n    * [Issue #847](https://github.com/openziti/sdk-golang/issues/847) - Ensure the initial version check succeeds, to ensure we don't legacy sessions on ha or oidc-enabled controllers\r\n    * [Issue #824](https://github.com/openziti/sdk-golang/pull/824) - release notes and hard errors on no TOTP handler breaks partial auth events\r\n\r\n* github.com/openziti/secretstream: [v0.1.41 -> v0.1.47](https://github.com/openziti/secretstream/compare/v0.1.41...v0.1.47)\r\n* github.com/openziti/storage: [v0.4.31 -> v0.4.38](https://github.com/openziti/storage/compare/v0.4.31...v0.4.38)\r\n    * [Issue #122](https://github.com/openziti/storage/issues/122) - StringFuncNode has incorrect nil check, allowing panic\r\n    * [Issue #120](https://github.com/openziti/storage/issues/120) - Change post tx commit constraint handling order\r\n    * [Issue #119](https://github.com/openziti/storage/issues/119) - Add ContextDecorator API\r\n\r\n* github.com/openziti/transport/v2: [v2.0.198 -> v2.0.209](https://github.com/openziti/transport/compare/v2.0.198...v2.0.209)\r\n* github.com/openziti/xweb/v3: [v2.3.4 -> v3.0.3](https://github.com/openziti/xweb/compare/v2.3.4...v3.0.3)\r\n    * [Issue #32](https://github.com/openziti/xweb/issues/32) - watched identities sometimes don't reload when changed\r\n\r\n* github.com/openziti/ziti/v2: [v1.7.0 -> v2.0.0](https://github.com/openziti/ziti/compare/v1.7.0...v2.0.0)\r\n    * [Issue #3599](https://github.com/openziti/ziti/issues/3599) - Add gap detection and handling to router data model\r\n    * [Issue #3074](https://github.com/openziti/ziti/issues/3074) - Dynamic cost range is too limited\r\n    * [Issue #3558](https://github.com/openziti/ziti/issues/3558) - terminator cost increased on egress dial success, not on circuit completion\r\n    * [Issue #3556](https://github.com/openziti/ziti/issues/3556) - global circuit costs not cleared when terminator is deleted\r\n    * [Issue #3557](https://github.com/openziti/ziti/issues/3557) - costing calculation for the weighted terminator selection strategy  is incorrect\r\n    * [Issue #2512](https://github.com/openziti/ziti/issues/2512) - Return circuit ID and error in dial failures\r\n    * [Issue #3569](https://github.com/openziti/ziti/issues/3569) - Version 2.0+ routers should not connect to controllers which do not support JWT formatted legacy sessions\r\n    * [Issue #3565](https://github.com/openziti/ziti/issues/3565) - Link dialer save 'is first conn' true, so all dials claim to be first, causing potential race condition\r\n    * [Issue #3550](https://github.com/openziti/ziti/issues/3550) - Support multi-underlay control channels\r\n    * [Issue #3535](https://github.com/openziti/ziti/issues/3535) - Remove the legacy xgress_edge_tunnel implementation\r\n    * [Issue #3547](https://github.com/openziti/ziti/issues/3547) - Add support for sending the dialing identity id and name to the hosting sdk\r\n    * [Issue #3541](https://github.com/openziti/ziti/issues/3541) - Remove option to disable the router data model in the controller\r\n    * [Issue #3540](https://github.com/openziti/ziti/issues/3540) - Handle UDP difference between proxy and tproxy implementations\r\n    * [Issue #3527](https://github.com/openziti/ziti/issues/3527) - ER/T UDP tunnels keep closed connections for 30s, preventing potential new good connections in that time\r\n    * [Issue #3526](https://github.com/openziti/ziti/issues/3526) - ER/T half-close logic is incorrect\r\n    * [Issue #3524](https://github.com/openziti/ziti/issues/3524) - Provide more error context to SDKs for terminator errors\r\n    * [Issue #3509](https://github.com/openziti/ziti/issues/3509) - Enforce policy on the router for oidc sessions, by closing open circuits and terminators when service access is lost\r\n    * [Issue #3531](https://github.com/openziti/ziti/issues/3531) - Remove created/updated/deleted terminator events. Obsoleted by entity change events.\r\n    * [Issue #3532](https://github.com/openziti/ziti/issues/3532) - Removed deprecated create identity <type> subcommands\r\n    * [Issue #3521](https://github.com/openziti/ziti/issues/3521) - Cleanup CLI to remove calls to os.Exit to be embed friendlier\r\n    * [Issue #3516](https://github.com/openziti/ziti/issues/3516) - Remove support for create terminator v1\r\n    * [Issue #3512](https://github.com/openziti/ziti/issues/3512) - Remove legacy link management code from the controller\r\n    * [Issue #3511](https://github.com/openziti/ziti/issues/3511) - router proxy mode fails to resolve interface if binding is 0.0.0.0\r\n    * [Issue #3503](https://github.com/openziti/ziti/issues/3503) - Allow routers to request current cluster membership information\r\n    * [Issue #3501](https://github.com/openziti/ziti/issues/3501) - Get cluster membership information from raft directly, rather than trying to cache it in the DB\r\n    * [Issue #3500](https://github.com/openziti/ziti/issues/3500) - Set a router data model timeline when initializing a new HA setup, rather than letting it stay blank\r\n    * [Issue #3504](https://github.com/openziti/ziti/issues/3504) - Reduce router data model full state updates\r\n    * [Issue #3492](https://github.com/openziti/ziti/pull/3492) - Bump openziti/ziti-console-assets from 3.12.9 to 4.0.0 in /dist/docker-images/ziti-controller in the all group\r\n    * [Issue #3484](https://github.com/openziti/ziti/issues/3484) - router ctrl channel handler for handling cluster changes has an initialization race condition\r\n    * [Issue #3477](https://github.com/openziti/ziti/issues/3477) - Optionally enable model changes triggered by login to be non-blocking and to be droppable if the system is under load\r\n    * [Issue #3473](https://github.com/openziti/ziti/issues/3473) - Enable tls handshake rate limiter by default and tweak default values.\r\n    * [Issue #3471](https://github.com/openziti/ziti/issues/3471) - Go tunneler is ignoring host config MaxConnections\r\n    * [Issue #3469](https://github.com/openziti/ziti/issues/3469) - Only send model updates on resubscribe if the RDM index has advanced\r\n    * [Issue #2573](https://github.com/openziti/ziti/issues/2573) - An edge router in a tight restart loop causes a resource leak on routers to which it connects.\r\n    * [Issue #3430](https://github.com/openziti/ziti/issues/3430) - Add permissions list to identity\r\n    * [Issue #2109](https://github.com/openziti/ziti/issues/2109) - Add Edge Management Read Only Capability\r\n    * [Issue #3435](https://github.com/openziti/ziti/issues/3435) - Add edge management API permissions by entity type and action\r\n    * [Issue #3441](https://github.com/openziti/ziti/issues/3441) - Update router connection tracker to interrogate active connections\r\n    * [Issue #3451](https://github.com/openziti/ziti/issues/3451) - ci - compare only stable releases when promoting\r\n    * [Issue #3437](https://github.com/openziti/ziti/issues/3437) - SDK OIDC token updates to routers should return an error if invalid\r\n    * [Issue #3348](https://github.com/openziti/ziti/issues/3348) - Unable to clear/reset the \"tags\" property on an entity to an empty object\r\n    * [Issue #3452](https://github.com/openziti/ziti/issues/3452) - `ziti agent cluster add` has bad behavior if the add address doesn't match the advertise address\r\n    * [Issue #3410](https://github.com/openziti/ziti/issues/3410) - Consolidate fabric REST API code with edge management and edge client code\r\n    * [Issue #3425](https://github.com/openziti/ziti/issues/3425) - RDM not properly responding to tunneler enabled flag changes\r\n    * [Issue #3420](https://github.com/openziti/ziti/issues/3420) - The terminator id cache uses the same id for all terminators in a host.v2 config, resulting in a single terminator\r\n    * [Issue #3419](https://github.com/openziti/ziti/issues/3419) - When using the router data model, precedence specified on the per-service identity mapping are incorrectly interpreted\r\n    * [Issue #3318](https://github.com/openziti/ziti/issues/3318) - Terminator creation seems to slow exponentially as the number of terminators rises from 10k to 20k to 40k\r\n    * [Issue #3407](https://github.com/openziti/ziti/issues/3407) - The CLI doesn't properly pass JWT authentication information to websocket endpoints\r\n    * [Issue #3359](https://github.com/openziti/ziti/issues/3359) - Ensure router data model subscriptions have reasonable performance and will scale\r\n    * [Issue #3381](https://github.com/openziti/ziti/issues/3381) - the fabric service REST apis are missing the maxIdleTime property\r\n    * [Issue #3382](https://github.com/openziti/ziti/issues/3382) - Legacy service sessions generated pre-1.7.x are incompatible with v1.7.+ and need to be cleared\r\n    * [Issue #3339](https://github.com/openziti/ziti/issues/3339) - get router ctrl.endpoint from ctrls claim in JWT\r\n    * [Issue #3378](https://github.com/openziti/ziti/issues/3378) - login with file stopped working\r\n    * [Issue #3346](https://github.com/openziti/ziti/issues/3346) - Fix confusing attempt logging\r\n    * [Issue #3337](https://github.com/openziti/ziti/issues/3337) - Router reports \"no xgress edge forwarder for circuit\"\r\n    * [Issue #3345](https://github.com/openziti/ziti/issues/3345) - Clean up connect events tests and remove global XG registry\r\n    * [Issue #3264](https://github.com/openziti/ziti/issues/3264) - Allow routers to generate alert events in cases of service misconfiguration\r\n","mentions_count":1}]