Repository navigation
Releases: haproxy-haptic/HAPTIC
Release list
v0.10.0
[0.10.0] - 2026-11-02
Added
fileRegistry.Register("secret", …)delivers a file to sidecars underspec.dataplane.secretsDir(default/etc/haproxy/secrets) without a reload. Its content is published only as a Kubernetes Secret, readable resources carry a keyed digest instead of a content digest, and thesecret:<name>validation test target asserts on it.haptic preflight --resources <path>and--resources-from-clusteralso render the configuration against the resources the controller will watch and runhaproxy -con the result (#586).
Fixed
- A map or file whose template reads a watched resource directly (such as with
GetSingle) or fetches over HTTP now picks up changes to that input after a render reused its previous output; before, it could keep stale content indefinitely (#604). - A render no longer panics when a template imports a snippet exporting a
varfunction both directly and from inside a macro; the snippet now initializes at whichever import runs first (#585). - After a release was only partly applied, the pre-rollout hook failed every later sync or upgrade with "finish the current apply before upgrading". It now validates the incoming release and applies it, which completes the partial one (#559).
Helm chart
Added
- Gateway API v1.7.0 support, including its
sessionPersistenceshape;sessionPersistence.cookie.pathsets the cookie'sPath. - A
secret-storagetmpfs volume at/etc/haproxy/secrets, mounted in the agent and SPOA hub containers but not in the HAProxy container. haproxy-haptic.org/sso-authsigns users in with OpenID Connect through the SPOA hub's sso-auth plugin, using a provider Secret in the Ingress's namespace;sso-auth-allowed-groupsadmits only members of the listed groups, andpass-tokenforwards the ID or access token asAuthorization: Bearerand refreshes it (#548).controller.config.templatingSettings.extraContext.ssoAuth.allowedIssuerslists the identity providers sso-auth provider Secrets may use.- Governance rules take a label
selectorto apply only to matching resources, anddefaultFromto pick a default by a value of the resource such as its hosts (#582). haproxy-haptic.org/error-pagesnames a ConfigMap whose pages replace the401,403,413, and429HAProxy sends when it refuses a request and the500,502,503, and504it generates for the Ingress's backends, as a staticfileor anlf-filelimited to operator-allowed expressions (#571, #577, #578).controller.config.templatingSettings.extraContext.customResponses.defaultErrorPagesnames a ConfigMap whose error pages apply to every Ingress; an Ingress's ownerror-pagesoverride it per status code (#583).controller.config.templatingSettings.extraContext.tls.requireSniHostMatchand thehaproxy-haptic.org/require-sni-host-matchannotation answer421when an HTTPS request's Host differs from its TLS SNI (#567).vector.requestMetrics.serverLabel: countersadds theserverlabel torequests_totalonly, keeping it off the histograms (#564).
Changed
- The CRDs moved from
crds/to thehaptic-crdssubchart (charts/haptic-crds/crds/), which keeps them out of the Helm release Secret; Helm still installs them, and theapply-crdshook still upgrades them (#588). - Update the bundled Vector image to 0.61.0.
- The controller requests
500mCPU instead of100m, so its startup validation finishes within the startup probe on a busy node (#570).
Fixed
- Restart the controllers when the SPOA hub plugin set changes, so their validator stops rejecting configurations that use a newly enabled plugin (#546).
- The
oauthannotations of the HAPTIC and haproxy-ingress libraries send the request's path and query asrdto oauth2-proxy's sign-in instead of the literal%[path]; a trailing$escaped_request_uriin anyauth-signinURL is expanded the same way (#550). - The
oauthannotations no longer apply external authentication to the Ingress's own oauth2-proxy path, which redirected browsers in a loop (#551). - The
vectorandspoa-hubsidecar health checks no longer trigger Falco's "Redirect STDOUT/STDIN to Network Connection in Container" rule every few seconds (#555). - Source allow-lists and deny-lists of every annotation family are enforced before the SPOA hub, so a refused client is denied before WAF, external authentication or OIDC sign-in (#568).
Security
- Plain HTTP to an Ingress that requires a client certificate is redirected to HTTPS instead of being served without verification; only ACME HTTP-01 challenge requests stay on HTTP.
v0.9.0
[0.9.0] - 2026-10-30
HAPTIC 0.9 pins release charts to signed image digests, splits the Grafana
dashboard into controller and traffic views, and fixes request-metric data loss
in the Vector sidecar.
Before upgrading: if you install from an image mirror, check that it keeps
the original image index. Read the
upgrade notes.
Helm chart
Added
- Optional
vector.requestMetrics.serverLabelidentifies the selected upstream server in request counters and duration histograms (#533).
Changed
- Published release charts select matching multi-platform controller image digests by default, including agents and lifecycle hooks; explicit image tags and digests retain precedence (#527).
- The built-in Grafana dashboard is split into controller and traffic dashboards, adding route, status, termination-state, latency, throughput, and backend-target panels. The
haptic.jsonConfigMap key andhapticUID remain the controller view;haptic-traffic.jsonand UIDhaptic-trafficare new. - Request counters use the conventional
_requests_totalname; the unsuffixed counter remains as a compatibility alias (#531).
Fixed
- Prometheus no longer drops Vector request metrics as out-of-order samples: the exporters omit sample timestamps (#537).
- Vector keeps request histogram counts when a configuration reload changes the metric pipeline inputs (#532).
- Vector forwards newline-terminated access logs without extra blank stdout records (#534).
- Grafana panels read registered configuration-validation outcomes and observability-drop counts instead of an absent histogram and an event-dependent subscriber series (#530).
v0.8.1
[0.8.1] - 2026-10-29
Fixed
- Identical configuration updates no longer discard passing validation results or restart retry backoff, avoiding redundant tests and false timeout rejections (#522).
Known issues
- An intermittent
HTTPRouteRequestMirrorconformance failure remains under investigation: affected runs did not observe mirrored requests in backend logs (#517).
v0.8.0
[0.8.0] - 2026-10-28
HAPTIC 0.8 adds client-certificate identity checks and headers for Ingress,
and serves TLSRoute termination and passthrough listeners on the same port.
It also closes Ingress client-certificate verification bypasses and tightens
trust of client-address headers.
Before upgrading: if you use src-ip-header, check which proxies may set
that header. Routes protected by client-certificate verification now enforce it
when using the default TLS certificate too. Read the
upgrade notes.
Added
validationTests.<name>.useDeploymentWatchScopeschecks fixtures against configured watch namespaces, including expected out-of-scope lookup rejections inhaptic preflight.
Fixed
- Validation tests no longer reject deterministic auxiliary files because their generated section markers change between renders.
Security
- Build with Go 1.27.3 to reject compromised intermediate certificates in TLS verification (CVE-2026-111943).
- Update
golang.org/x/netto 0.62.0 for HTML parsing and rendering security fixes (#519).
Helm chart
Added
- Serve TLSRoute termination and passthrough listeners on one port with isolated certificates and client-validation policies.
haproxy-haptic.org/auth-tls-match-subjectlimits a host to client certificates whose subject DN matches a regular expression.haproxy-haptic.org/auth-tls-cn-headerforwards the verified client certificate's CN in a request header you name, such asX-Scope-OrgID.- Pin controller, agent, and lifecycle hook images with
controller.image.digest.
Changed
- BREAKING:
src-ip-headerno longer trusts client-sentX-Real-IP,X-Client-IP, orClient-IPby itself. List the L7 proxy inforwardedHeaders.trustedProxiesor produce the header in an operator snippet; L4 PROXY-protocol client addresses need no header annotation. - Update the bundled Vector image to 0.60.0.
Fixed
- Forward Ingress client-certificate headers on resumed TLS sessions and when annotation libraries coexist.
- HTTPRoute hostname specificity takes precedence over path length and header conditions when routes intersect the same listener hostname.
- Keep TLS passthrough listener isolation on the shared HTTPS address after routes are removed or listener modes change; unmatched Gateway names still reach Ingress routes.
Security
- Enforce Ingress client-certificate verification with default TLS certificates, including when the certificate names the protected host.
- Reject requests to a client-certificate-protected Ingress with
421when TLS SNI names another host or is absent, instead of bypassing verification. - Drop untrusted client-sent
Forwarded,X-Forwarded-Prefix,X-Forwarded-Server,X-Real-IP,X-Client-IP, andClient-IPheaders, while preserving trusted proxies and operator header changes (#490).
Known issues
- An intermittent
HTTPRouteRequestMirrorconformance failure remains under investigation: affected runs did not observe mirrored requests in backend logs (#517).
v0.7.0
[0.7.0] - 2026-10-20
HAPTIC 0.7 hardens two trust boundaries: raw HAProxy configuration annotations,
which let an Ingress writer change the configuration for every host, need an
explicit opt-in, and every HTTP frontend replaces client-sent forwarded headers,
so clients can't forge their address. Chart upgrades roll the controller to the
new image before applying new templates.
Before upgrading: check your Ingresses for raw configuration annotations and
haproxy-ingress.github.io/forwardfor, and list any L7 proxies in front of
HAPTIC. Read the upgrade notes.
Added
watchedResources.<name>.namespacesrestricts watches and template lookups to listed namespaces;namespacesSettingidentifies the setting in rejection messages.- Metrics:
haptic_config_validated{status,reason}: each replica'sValidatedcondition, one series per state, so an alert can match a configuration stuck atUnknownorFalse.haptic_config_validation_total{result}:valid,invalid, andinconclusiveverdicts on theHAProxyTemplateConfig, the startup load gate included.haptic_validation_errors_totalcounts only HAProxy's verdicts on rendered configurations.
on_own_lines(render_glob "prefix-*")template function: renders the matching snippets so that each starts on a new line.
Changed
haptic_render_totalandhaptic_render_errors_totalexport everycache_stateat zero, andhaptic_config_rejected_totaleveryvalidator, soincrease()and theHAProxyControllerConfigRejectedalert see the first failure.- A change to a server's
maxconnis applied with HAProxy's runtime API instead of a reload. - The controller logs
Reload requiredat INFO with the pod and the reasons whenever it reloads a pod, instead of only at DEBUG.
Fixed
haptic preflightvalidates sidecar configurations even when a restrictive umask would prevent the container user from reading temporary files.- When an upgrade adds runtime operations, older agents still apply supported runtime changes without a reload.
- Frequent per-pod status writes no longer block publication of a changed
HAProxyCfgwith resource-version conflicts. - An unchanged successful render-gate verdict no longer rewrites
HAProxyCfgstatus on every render. - A change to the credentials Secret that leaves
dataplane_usernameanddataplane_passwordunchanged, such as every Helm upgrade, no longer restarts the controller iteration, which briefly doubled controller memory and could OOMKill large installs (#456). - A controller container restarted during its startup validation no longer fails every
validationTestwithno space left on device. Each test's scratch files are removed when it finishes, instead of when the whole suite does. - A controller could stop deploying, leaving HAProxy pods unready, when the render gate's verdict reached the deployment scheduler before the render it named. Each component now receives an event before any reaction to it.
- A configuration change on a large install no longer hands leadership to a replica with a cold render graph while the warm-up render is still running, which delayed deployments until the new leader rendered from scratch.
- Deployments no longer contend with leader Lease renewals or log spurious
Failed to update leaseerrors. - A new leader preserves the latest configuration validation verdict instead of reporting an older accepted configuration as valid over a newer rejected or unvalidated generation.
- Configuration changes fetch watched-resource schemas in parallel, and API discovery timeouts leave validation inconclusive and retry it instead of rejecting templates that use typed fields.
- Admission denials show the governance rule's message and the watched object; other render errors identify the watched object without exposing encoded cache keys.
- Startup-validation messages describe retrying NotReady pods and stalled rollouts instead of claiming the controller exits on rejection.
- A deployment cancelled because the HAProxy pod set changed no longer logs
context cancelederrors, emitsApplyFailedPod events, or counts inhaptic_deployment_errors_total(#430). - Expected events log at INFO instead of WARN: config publisher API calls cancelled by its own shutdown, the wait for the render gate's first verdict after a start or configuration change, and a leader drained during a configuration hand-over (#444, #445, #453).
Helm chart
Added
extraContext.forwardedHeaders.trustedProxies: the L7 load balancers whoseX-Forwarded-For,-Proto,-Host, and-Portheaders HAPTIC keeps.controller.rbac.releaseNamespaceSecretsOnlylimits Secret reads to the release namespace and rejects references elsewhere.- A watched resource that lists
namespacesis granted by a Role in each listed namespace instead of the ClusterRole. HAProxyControllerConfigNotValidatedalert (controller.monitoring.prometheusRule.defaultRules.configNotValidated): a replica hasn't validated its newestHAProxyTemplateConfigfor 10 minutes.
Changed
- The pre-upgrade hook rolls the controller Deployment to the new image before Helm applies new templates, so old replicas no longer reject a valid incoming configuration. See controller preparation for custom image tags and deployments without hooks.
- BREAKING: haproxy-ingress:
haproxy-ingress.github.io/forwardfor: addreplaces the client'sX-Forwarded-Forandupdateappends to it, as upstream does; HAPTIC had them the other way round. Check your Ingresses before upgrading. - An Ingress that sets
X-Forwarded-Forhandling through annotations from two families is rejected at admission, like other mixed-family features. - A
helm upgradethat changes only the chart version no longer changes theHAProxyTemplateConfig, so running controllers don't start a new iteration mid-rollout. The controller-managed HAProxy Service drops itshelm.sh/chart,app.kubernetes.io/version, andapp.kubernetes.io/managed-bylabels. - Controller pods no longer carry the
helm.sh/chartandapp.kubernetes.io/versionlabels; the Deployment keeps them. A chart upgrade then rolls the controllers once instead of twice; the upgrade to 0.7.0 itself still rolls them twice (#467).
Fixed
- Preflight compiles Varnish configurations with the deployed image when caching is enabled and a released fixture image when it is disabled.
- Custom TCP frontends that accept PROXY protocol pass validation when the built-in HTTP/HTTPS PROXY listeners are disabled.
haproxy.org/pod-maxconnandhaproxy-haptic.org/pod-maxconnsetmaxconnon each server line, so a change in the ready HAProxy pod count (for example during a rollout) updates it at runtime instead of reloading every HAProxy pod (#474).haproxy-haptic.org/forwardforandhaproxy-ingress.github.io/forwardfor:addandupdateno longer send the client address twice, andifmissingandignoreno longer append it to the client's header.- haproxytech:
haproxy.org/forwarded-for: "false"forwards the client'sX-Forwarded-Forunchanged; it was ignored. - A snippet at an extension point starts on its own line whether or not its template begins or ends with a newline. A
frontend-extra-*or other snippet without a leading newline could join the previous line and fail validation.
Security
- BREAKING: Raw HAProxy configuration annotations (
haproxy-haptic.org/config-*,haproxy-ingress.github.io/config-*,haproxy.org/backend-config-snippet, andnginx.ingress.kubernetes.io/configuration-snippet) are rejected unlesscontroller.config.templatingSettings.extraContext.ingressPermissions.allowRawHAProxyConfigistrue. Any Ingress writer in a watched namespace could otherwise change the configuration for every host or read HAProxy's TLS keys.waf.ingressPermissions.allowRawHAProxyConfigcan only narrow it. Check your Ingresses before upgrading. - WAF governance's
allowRawHAProxyConfig: falsealso rejects raw configuration annotations when thehapticAnnotationslibrary is disabled. - BREAKING: Every HTTP frontend replaces a client-sent
X-Forwarded-Forwith the client address, setsX-Forwarded-Prototohttpsorhttp, and drops a client-sentX-Forwarded-HostandX-Forwarded-Port, so clients can't forge their address or scheme (#406).X-Forwarded-Host,-Port, and-Protoset byfrontend-extra-*snippets are kept. Behind an L7 load balancer, list it intrustedProxies;trustedProxies: ["0.0.0.0/0", "::/0"]restores the oldX-Forwarded-Forhandling.
v0.6.0
[0.6.0] - 2026-10-18
HAPTIC 0.6 moves to GitHub, requires Kubernetes 1.35 or newer, and drops
HAProxy 3.1. It ships security updates for Go, HAProxy, and the bundled Coraza
WAF plugin, and makes retained recovery hold up through ordinary leader changes
and critical http.Fetch outages.
Before upgrading: check your Kubernetes version, move off HAProxy 3.1, and
remove any registry.gitlab.com image overrides. Read the
upgrade notes.
Added
- Metrics:
haptic_render_errors_total: failed reconcile renders on every replica, by cache state.haptic_haproxy_checks_totalandhaptic_haproxy_checks_reused_total:haproxy -cruns per validator and result on every replica, and the runs answered by an identical input's verdict instead.haptic_admission_render_slot_wait_seconds: how long admission dry runs queue for a render slot.
- Select several validation tests in one run by repeating
haptic validate --test. MUTEX_PROFILE_FRACTIONandBLOCK_PROFILE_RATEenable the mutex and block profiles on/debug/pprof.
Changed
- BREAKING: The Go module path is now
github.com/haproxy-haptic/HAPTIC(wasgitlab.com/haproxy-haptic/haptic). Programs that import HAPTIC packages must update their import paths. - Development, issues, releases, and container images moved to GitHub: github.com/haproxy-haptic/HAPTIC and
ghcr.io/haproxy-haptic. Releases up to 0.5.0 are available there with their original digests and signatures. - Reconciliation CPU and latency no longer grow with the size of the watched set: input isolation inspects only the resources changed since its last capture.
- The leader runs
haproxy -conce per render instead of twice: the render gate reuses the verdict input acceptance just gave the identical config and files. - Recovery checkpoints share unchanged auxiliary files instead of copying every map, file, and certificate Secret, and publications read auxiliary files with one list per kind. Steady deployment churn no longer creates a full object set per checkpoint, and most of the controller's API requests are gone.
- Drift prevention no longer re-applies every HAProxy pod's unchanged status to the
HAProxyCfgand each auxiliary file every interval: it reads them live and corrects only entries that changed. On an idle fleet this removes about two thirds of the controller's API requests. - Startup validation may use 80% of the controller's startup probe window (240 seconds by default) instead of a fixed 120 seconds, so a large validation-test suite on a small node no longer crash-loops the controller.
- Failed renders count in
haptic_render_total, and failed reconciliations inhaptic_reconciliation_duration_seconds. A follower warns when its renders start failing. haptic_webhook_request_duration_secondsbuckets extend to 30 seconds, so latencies near the admission timeout are measurable.- Validation test totals and results are logged at INFO.
- Fewer allocations during input isolation, validation, and rendering.
Fixed
- Retained recovery:
- A leader that stops gracefully finishes its deployment in progress, including a pending HAProxy reload, and saves its recovery checkpoint before it releases the Lease. Previously, after an ordinary leader change, replacement HAProxy pods could stay NotReady with
RetainedConfigUnavailablewhile rendering was blocked. - A verified
/v1/stateread that overlapped an apply could restore the dataplane agent's previous file set, so the pod reporteddisk_is_the_desired_setuntil it restarted and new pods could not recover from the checkpoint. - Recovery starts after leader failover when a critical HTTP source is unavailable, including with large watched sets.
- Pruned checkpoints are recreated when their configuration is deployed again, and retained configurations no longer exceed the storage limit once recovery plans are included.
- A leader that stops gracefully finishes its deployment in progress, including a pending HAProxy reload, and saves its recovery checkpoint before it releases the Lease. Previously, after an ordinary leader change, replacement HAProxy pods could stay NotReady with
- Critical
http.Fetchoutages: a replica without the source's content no longer renders, or re-reads every watched resource, on every change. It refetches the source everyinterval(30 seconds by default) and renders when the source answers. - A standby replica that became leader after more than 1.5 ×
driftPreventionIntervalfailed liveness until it deployed, so the kubelet restarted it, for example while a criticalhttp.Fetchsource was down. - An idle drift-prevention round no longer resends and rewrites every file on every HAProxy pod: the dataplane agent's verified read keeps a file's proof while the file still has the bytes, mode, and owner the agent wrote.
- Valid changes are no longer denied with a timeout during heavy resource churn: a CPU stays free for each admission render.
- A schema fetch timeout no longer rejects a valid HAProxyTemplateConfig change for good: it shows
Validated=Unknown(ValidationInconclusive) and is validated again with backoff. Schema reads fetch only the watched CRDs. - A configuration that fails
haproxy -cor rendering independently of watched resources is reported after two checks at startup instead of one per watched object. - A cold render cancelled while a template published a shared value no longer deadlocks the replica's first render, which left admission denied.
- Shared rate limiting no longer falls back to per-pod budgets for 5 seconds after one slow store reply (rate-limit plugin v0.4.2).
- The dataplane agent no longer reports ready before its admin and drain sockets exist, and no longer stalls 30 seconds when HAProxy is still starting its worker.
/livezand/readyzsay why a component is unhealthy.- Auxiliary-file
deployedToPodsentries name only the pods that run each file. A pod's entry no longer goes missing until the next drift check when its status lands before or while its configuration is published, and no longer lands on the previous configuration's files. - Queued events no longer delay controller shutdown.
- The playground escapes quotes in resource, annotation, and template names, so a shared link cannot inject HTML attributes.
- A new leader deploys a critical
http.Fetchsource's first content as soon as its replica accepts it. Previously, if the content was accepted just after the leader's first render, HAProxy pods stayed NotReady until the next drift-prevention round (60 seconds by default), for example right after the controller started.
Security
- Go 1.27.2 and its HTTP/2 dependency fix HTTP, TLS, and filesystem vulnerabilities.
- HAProxy 3.0.30, 3.2.26, 3.3.17, and 3.4.7 fix HTTP/2 PING frame handling and an over-long HTTP/1 chunk-size line, both marked major upstream.
- The bundled Coraza SPOA plugin v0.10.1 is built against Coraza v3.8.1 and fixes WAF bypass and crash vulnerabilities (GHSA-6gcq-wc29-5xf2, GHSA-6r3q-mjv7-xr8m, and ten Medium advisories). It also stops a memory leak when the validator rechecks an invalid WAF policy.
Helm chart
Added
- Opt-in Pod readiness gate
haproxy-haptic.org/load-balancer-ready(controller.templateLibraries.kubernetesBackends.podReadinessGate.enabled): a gated pod becomes Ready only once every HAProxy pod routes to it, so single-replica rollouts drop no requests.
Changed
- BREAKING: The chart requires Kubernetes 1.35 or newer; 1.33 is past its upstream end of life and 1.34 reaches it on 2026-10-27. Check your cluster version before upgrading.
- BREAKING: HAProxy 3.1 is no longer supported: it is past its upstream end of life and misses the 2026-10-14 HTTP/1 and HTTP/2 fixes. Helm rejects
haproxyVersion: "3.1"; switch to 3.2 or 3.4 before upgrading. - Default controller, SPOA hub, and Varnish images come from
ghcr.io/haproxy-haptic/haptic, and the chart is published atoci://ghcr.io/haproxy-haptic/haptic/charts/haptic; itshomeandsourceslinks point to GitHub. - Render the Helm chart faster by avoiding repeated YAML serialization of template libraries.
- Default controller, SPOA hub, and Varnish images come from
ghcr.io/haproxy-haptic/haptic, and the chart is published atoci://ghcr.io/haproxy-haptic/haptic/charts/haptic. - The chart renders faster.
Fixed
- The controller NetworkPolicy accepts webhook calls from any source by default. Its
0.0.0.0/0and::/0rules don't match node traffic on Cilium, so admission calls from other nodes timed out.controller.networkPolicy.ingress.webhook.fromstill restricts the webhook port when set. - With several releases in one cluster, each release's Ingress and Gateway status listed every release's load balancer addresses, so external-dns published names with other releases' IPs. Each controller now watches only its own release's load balancer Services.
- TLS Gateways use isolated service addresses and pod ports, so they can't route to another Gateway's TLSRoutes.
- Gateway API: TLSRoutes keep their hostname specificity after listener intersection, with older routes winning ties.
- Pre-rollout validation and the load gate no longer fail with
vector.enabled=false,vector.requestMetricsfeatures off,spoaHub.enabled=false, or the mirror plugin off. - haproxy-ingress: an Ingress with
haproxy-ingress.github.io/waforwaf-modefails the render whenspoaHub.enabled=falseinstead of being served without WAF inspection. - nginx-ingress:
nginx.ingress.kubernetes.io/enable-modsecurity: "true"sends the route to the Coraza WAF in detect mode, like ingress-nginx's default; it was ignored.
v0.6.0-rc.2
[0.6.0-rc.2] - 2026-10-17
HAPTIC 0.6 moves to GitHub, requires Kubernetes 1.35 or newer, and drops
HAProxy 3.1. It ships security updates for Go, HAProxy, and the bundled Coraza
WAF plugin, and makes retained recovery hold up through ordinary leader changes
and critical http.Fetch outages.
Before upgrading: check your Kubernetes version, move off HAProxy 3.1, and
remove any registry.gitlab.com image overrides. Read the
upgrade notes.
Added
- Metrics:
haptic_render_errors_total: failed reconcile renders on every replica, by cache state.haptic_haproxy_checks_totalandhaptic_haproxy_checks_reused_total:haproxy -cruns per validator and result on every replica, and the runs answered by an identical input's verdict instead.haptic_admission_render_slot_wait_seconds: how long admission dry runs queue for a render slot.
- Select several validation tests in one run by repeating
haptic validate --test. MUTEX_PROFILE_FRACTIONandBLOCK_PROFILE_RATEenable the mutex and block profiles on/debug/pprof.
Changed
- BREAKING: The Go module path is now
github.com/haproxy-haptic/HAPTIC(wasgitlab.com/haproxy-haptic/haptic). Programs that import HAPTIC packages must update their import paths. - Development, issues, releases, and container images moved to GitHub: github.com/haproxy-haptic/HAPTIC and
ghcr.io/haproxy-haptic. Releases up to 0.5.0 are available there with their original digests and signatures. - Reconciliation CPU and latency no longer grow with the size of the watched set: input isolation inspects only the resources changed since its last capture.
- The leader runs
haproxy -conce per render instead of twice: the render gate reuses the verdict input acceptance just gave the identical config and files. - Recovery checkpoints share unchanged auxiliary files instead of copying every map, file, and certificate Secret, and publications read auxiliary files with one list per kind. Steady deployment churn no longer creates a full object set per checkpoint, and most of the controller's API requests are gone.
- Drift prevention no longer re-applies every HAProxy pod's unchanged status to the
HAProxyCfgand each auxiliary file every interval: it reads them live and corrects only entries that changed. On an idle fleet this removes about two thirds of the controller's API requests. - Startup validation may use 80% of the controller's startup probe window (240 seconds by default) instead of a fixed 120 seconds, so a large validation-test suite on a small node no longer crash-loops the controller.
- Failed renders count in
haptic_render_total, and failed reconciliations inhaptic_reconciliation_duration_seconds. A follower warns when its renders start failing. haptic_webhook_request_duration_secondsbuckets extend to 30 seconds, so latencies near the admission timeout are measurable.- Validation test totals and results are logged at INFO.
- Fewer allocations during input isolation, validation, and rendering.
Fixed
- Retained recovery:
- A leader that stops gracefully finishes its deployment in progress, including a pending HAProxy reload, and saves its recovery checkpoint before it releases the Lease. Previously, after an ordinary leader change, replacement HAProxy pods could stay NotReady with
RetainedConfigUnavailablewhile rendering was blocked. - A verified
/v1/stateread that overlapped an apply could restore the dataplane agent's previous file set, so the pod reporteddisk_is_the_desired_setuntil it restarted and new pods could not recover from the checkpoint. - Recovery starts after leader failover when a critical HTTP source is unavailable, including with large watched sets.
- Pruned checkpoints are recreated when their configuration is deployed again, and retained configurations no longer exceed the storage limit once recovery plans are included.
- A leader that stops gracefully finishes its deployment in progress, including a pending HAProxy reload, and saves its recovery checkpoint before it releases the Lease. Previously, after an ordinary leader change, replacement HAProxy pods could stay NotReady with
- Critical
http.Fetchoutages: a replica without the source's content no longer renders, or re-reads every watched resource, on every change. It refetches the source everyinterval(30 seconds by default) and renders when the source answers. - A standby replica that became leader after more than 1.5 ×
driftPreventionIntervalfailed liveness until it deployed, so the kubelet restarted it, for example while a criticalhttp.Fetchsource was down. - An idle drift-prevention round no longer resends and rewrites every file on every HAProxy pod: the dataplane agent's verified read keeps a file's proof while the file still has the bytes, mode, and owner the agent wrote.
- Valid changes are no longer denied with a timeout during heavy resource churn: a CPU stays free for each admission render.
- A schema fetch timeout no longer rejects a valid HAProxyTemplateConfig change for good: it shows
Validated=Unknown(ValidationInconclusive) and is validated again with backoff. Schema reads fetch only the watched CRDs. - A configuration that fails
haproxy -cor rendering independently of watched resources is reported after two checks at startup instead of one per watched object. - A cold render cancelled while a template published a shared value no longer deadlocks the replica's first render, which left admission denied.
- Shared rate limiting no longer falls back to per-pod budgets for 5 seconds after one slow store reply (rate-limit plugin v0.4.2).
- The dataplane agent no longer reports ready before its admin and drain sockets exist, and no longer stalls 30 seconds when HAProxy is still starting its worker.
/livezand/readyzsay why a component is unhealthy.- A pod's
deployedToPodsentry no longer goes missing from the auxiliary-file resources until the next drift check when its status lands while theHAProxyCfgis being published. - Queued events no longer delay controller shutdown.
- The playground escapes quotes in resource, annotation, and template names, so a shared link cannot inject HTML attributes.
Security
- Go 1.27.2 and its HTTP/2 dependency fix HTTP, TLS, and filesystem vulnerabilities.
- HAProxy 3.0.30, 3.2.26, 3.3.17, and 3.4.7 fix HTTP/2 PING frame handling and an over-long HTTP/1 chunk-size line, both marked major upstream.
- The bundled Coraza SPOA plugin v0.10.1 is built against Coraza v3.8.1 and fixes WAF bypass and crash vulnerabilities (GHSA-6gcq-wc29-5xf2, GHSA-6r3q-mjv7-xr8m, and ten Medium advisories). It also stops a memory leak when the validator rechecks an invalid WAF policy.
Helm chart
Added
- Opt-in Pod readiness gate
haproxy-haptic.org/load-balancer-ready(controller.templateLibraries.kubernetesBackends.podReadinessGate.enabled): a gated pod becomes Ready only once every HAProxy pod routes to it, so single-replica rollouts drop no requests.
Changed
- BREAKING: The chart requires Kubernetes 1.35 or newer; 1.33 is past its upstream end of life and 1.34 reaches it on 2026-10-27. Check your cluster version before upgrading.
- BREAKING: HAProxy 3.1 is no longer supported: it is past its upstream end of life and misses the 2026-10-14 HTTP/1 and HTTP/2 fixes. Helm rejects
haproxyVersion: "3.1"; switch to 3.2 or 3.4 before upgrading. - Default controller, SPOA hub, and Varnish images come from
ghcr.io/haproxy-haptic/haptic, and the chart is published atoci://ghcr.io/haproxy-haptic/haptic/charts/haptic; itshomeandsourceslinks point to GitHub. - Render the Helm chart faster by avoiding repeated YAML serialization of template libraries.
- Default controller, SPOA hub, and Varnish images come from
ghcr.io/haproxy-haptic/haptic, and the chart is published atoci://ghcr.io/haproxy-haptic/haptic/charts/haptic. - The chart renders faster.
Fixed
- The controller NetworkPolicy accepts webhook calls from any source by default. Its
0.0.0.0/0and::/0rules don't match node traffic on Cilium, so admission calls from other nodes timed out.controller.networkPolicy.ingress.webhook.fromstill restricts the webhook port when set. - With several releases in one cluster, each release's Ingress and Gateway status listed every release's load balancer addresses, so external-dns published names with other releases' IPs. Each controller now watches only its own release's load balancer Services.
- TLS Gateways use isolated service addresses and pod ports, so they can't route to another Gateway's TLSRoutes.
- Gateway API: TLSRoutes keep their hostname specificity after listener intersection, with older routes winning ties.
- Pre-rollout validation and the load gate no longer fail with
vector.enabled=false,vector.requestMetricsfeatures off,spoaHub.enabled=false, or the mirror plugin off. - haproxy-ingress: an Ingress with
haproxy-ingress.github.io/waforwaf-modefails the render whenspoaHub.enabled=falseinstead of being served without WAF inspection. - nginx-ingress:
nginx.ingress.kubernetes.io/enable-modsecurity: "true"sends the route to the Coraza WAF in detect mode, like ingress-nginx's default; it was ignored.
Website publication archives
Verified website content. The website-state branch records the published version catalog.
Website migration baseline, 13 October 2026
Complete static-site baseline for migrating haproxy-haptic.org to GitHub Pages.
The archive contains 4,196 files, all eight retained playground engines, and a manifest recording file checksums and source provenance. It restores 1,319,394,736 bytes. The GitHub Pages packaging step retains Brotli engine copies and verifies their decoded bytes against this baseline.
The source export was assembled during migration verification run 38424077823, including historical engines restored from the published site. This baseline is independent of Actions artifact retention.
Archive SHA-256: f9848bc4526524fafae16dc83ca990f55584265c85d0878e0d57de768cbc5422.
v0.5.0
[0.5.0] - 2026-10-08
HAPTIC 0.5 can restore HAProxy capacity during a controller restart or leader
change even when rendering is blocked. New pods load a retained, acknowledged
configuration after ownership, freshness, and current-HAProxy validation checks.
Before upgrading: retained recovery needs a successful deployment by a 0.5
controller before it can protect replacement pods. Read the
upgrade notes for this requirement
and the limits of serving retained endpoint lists.
Added
- New HAProxy pods can load an acknowledged configuration retained in Kubernetes when a fresh leader cannot render, with current-binary validation, Warning events, and
haptic_retained_config_active. /healthzincludes the last render failure without changing startup, readiness, or liveness decisions.
Fixed
- Reduce controller startup time and temporary allocations while preserving every validation check and the existing memory budget.
- Endpoint updates and admission requests no longer wait for each other's HAProxy validation checks.
- Prevent repeated status writes across reconciliation phases from making valid Kubernetes updates time out.
- Verified agent-state reads no longer race HAProxy reloads and invalidate the confirmed worker configuration.
Helm chart
Added
- The bundled PrometheusRule alerts when a controller uses retained configuration while rendering is blocked.
Fixed
- Gateway API: catch-all GRPCRoutes select the most specific hostname even when a broader route is older or its name sorts first.