An overview of the latest Varnish Artifact Firewall releases, describing feature additions, changes, fixes and removals per version.
Released: 2026-10-22
license.file or license.text): a Varnish Artifact Firewall license, or a Varnish Enterprise license with the sup-firewall addon. It refuses to start without one, and honours the license’s expiration, no_restart and shutdown dates as varnishd does. A Varnish Artifact Firewall license with a version line only licenses that exact firewall version.normal refuses.cached and decided_at.firewall_audit_entries_total metric, labelled by result (written/failed). Nothing is counted while audit logging is off.audit_log_output file that cannot be opened now stops the firewall from starting. It used to start with audit logging off.v, such as vers:go/<1.64.0 as every OSV advisory writes it, never matching, because the go command always asks for v1.63.0. A range written with the v, such as vers:go/>=v1.2.0, also wrongly missed a version without it. Go ranges and versions now compare the same with or without the v.Released: 2026-10-05
id, ts, kind, registry, mode, outcome, rule_system_id and ruleset_system_id.effective_action field is now enforced_action, and the span attribute firewall.effective_action is now firewall.enforced_action.ruleset "npm-policy": rule "block-leftpad": severity: 11.0 must be between 0 and 10.api_address the firewall cannot bind now stops it at startup, with the reason in the log. It used to keep serving proxy traffic with the admin API unreachable and nothing but one line to say so./healthz and /readyz are no longer counted under the route label on the HTTP metrics./api/rulesets/{ruleset_id}/rules/{rule_id} now uses the same field names a ruleset uses: a selector carries version rather than version_range, the registry scope is gone, and unset fields are omitted. PATCH to that path now rejects fields it does not know, so a body captured from an older version has to drop version_range and the registry scope before it is sent back.pkg:nuget/Newtonsoft.Json, never matching, because the firewall lowercases the requested id. NuGet names now match in any case, in traffic and in /api/eval, whose audit entries log the NuGet purl lowercased, like traffic.action: DENY in a rule or default_action: DENY in the config, loading without complaint and then never being enforced, so the package it was meant to block was served.rules: crashing the firewall instead of being reported as invalid.Released: 2026-09-09
--version now carry the revision the binary was built from, as stamped by the Go toolchain, suffixed -dirty for a build off a modified tree. It is omitted when the build carries no stamp.@v/list, @v/<version>.{info,mod,zip}, and @latest requests are identity-extracted, evaluated against rulesets, and allowed/hidden/denied like any other package type. @v/list is content-filtered to drop denied/hidden versions; .info/.mod are gated without altering their bytes, so GOSUMDB verification keeps working through the firewall. See the README for the GOPROXY single-entry requirement (no ,direct fallback) that this protection depends on.repodata.json, and .conda/.tar.bz2 downloads are blocked at 403; repodata that can’t be filtered (.bz2, CEP-16 shards) falls back to 404, and unparseable repodata fails open (streamed through unfiltered) since the artifact preflight still blocks a denied package’s download regardless. The channel is the purl namespace (pkg:conda/<channel>/<name>@<version>). Fetching a conda-forge-scale index needs max_manifest_size raised above the 64MiB default.INFO is reserved for state changes rather than a line before and another after each one. A credential in firewall.webhook.headers is no longer written to the log.log_output file that is the first place an operator looks. It is now an ERROR record, written before the log it would go to is closed, and the line saying the firewall stopped cleanly is written there too, where it used to be lost the same way.api_address already in use left the firewall serving traffic with no admin API and nothing in the log about it.deployment having its startup configuration reported as neither standalone nor integrated.Released: 2026-08-21
Released: 2026-08-20
repomd.xml and filters primary.xml, recomputing SHA256 sums so dnf’s integrity check still passes. .rpm downloads are blocked at 403 when a deny rule applies. repo_gpgcheck=0 is required on the client because rewriting repomd invalidates its signature; per-package gpgcheck=1 continues to work. Set preserve_manifest: true to keep the upstream repomd signature intact instead: metadata is then passed through unfiltered, but .rpm downloads are still blocked. Supports uncompressed, gzip, zstd, and xz metadata (bz2 streams through unmodified). filelists.xml/other.xml are left untouched and stream through unchanged.sub_paths list alongside sub_path, and either may name a directory, which contributes every .yaml/.yml file under it at any depth. Taking six rulesets from a repository such as varnish/osv-rules is now one source that clones and caches it once, where it previously took six sources, each keeping its own copy of the same repository. Rulesets added to a tracked directory later are picked up without a config change.date: field is no longer reloaded.Released: 2026-07-31
auth.github_app (a client ID and a private key by path or env). The firewall discovers the App installation from the repository URL, then mints and refreshes short-lived installation tokens automatically. api_base_url supports GitHub Enterprise Server./packages/<name>) is a signed protobuf payload that the client cryptographically verifies, so unlike PyPI/NuGet/Maven, denied or vulnerable versions can’t be filtered out of it (any change to the payload breaks the signature and hex_core rejects the response outright); enforcement is instead a per-tarball allow/deny check on download.otel: block whose schema matches Varnish Supervisor’s, so the same config works standalone or integrated. otel.metrics.exporter selects otlp (push) or prometheus (scrape). Standalone stands up its own OTel SDK. Integrated reuses Supervisor’s MeterProvider, where firewall metrics appear under instrumentation scope artifact-firewall.otel.tracing (off until enabled with an endpoint). Per request the firewall emits an HTTP server span, a firewall.proxy span, a firewall.transform_manifest span, and an upstream client span. The admin /api/eval endpoint emits a firewall.evaluate span with the rule decision. Inbound W3C traceparent headers are honored and propagated upstream. Standalone stands up its own TracerProvider. Integrated reuses Supervisor’s.audit_log_output: otel to export the audit log through the OTel logs pipeline (configured under otel.logs) instead of JSON. Each entry becomes an OTel log record under its own scope artifact-firewall/audit (fields as attributes, severity by action), correlated with the request trace. Standalone stands up its own LoggerProvider. Integrated reuses Supervisor’s.log_output: otel to export process logs through the OTel logs pipeline under scope artifact-firewall/log. This tees to stderr (startup logs are never lost) and also exports to OTel. log_output is now honored generally (stdout by default, stderr, or a file path), having previously been ignored. File-based process logs are closed on shutdown and reopened on SIGHUP for external rotation, matching the audit log.otel.metrics instead of the top-level metrics_address. otel.metrics.exporter selects otlp (default, push) or prometheus (scrape endpoint at prometheus_host:prometheus_port, default localhost:9464). In integrated mode the firewall reuses Supervisor’s metrics pipeline.metrics_address: ":9090" takes two settings: an endpoint or exporter: prometheus, since metrics are no longer exported by default, and prometheus_host: "0.0.0.0", since the scrape endpoint now binds loopback only.audit_log_output/log_output set to otel without otel.logs.metrics_address config field is deprecated and inert: it still parses (logging a warning) but has no effect. Configure metrics under otel.metrics..pom of every candidate version while resolving version conflicts, including versions it then discards, so returning 403 for a denied .pom failed the whole resolve. .pom files (and Gradle’s .module) now stream through unchanged, and a deny is enforced on the artifact itself whatever its packaging type, so a build that pins a denied version still gets a 403.auth.token_env is no longer passed to git subprocesses, where the token was readable through /proc/<pid>/environ and by anything git spawned (credential helpers from a system gitconfig, pagers, filters). The token still reaches the remote as an HTTP header.Released: 2026-07-14
Released: 2026-07-10
pypi.api_url).Released: 2026-06-30
hide verdict no longer returns 403 for a pinned .pom/snapshot; hidden versions are dropped from maven-metadata.xml but a pinned request still resolves, matching npm/PyPI/NuGet. Only deny hard-blocks.Released: 2026-06-13
.../api/<type>/<repo>/) is stripped before identification.maven-metadata.xml and .pom files are evaluated against the coordinates in the document body, not the URL path.registration / flatcontainer / query / index.json), which match Artifactory’s layout as well as nuget.org’s.Content-Type the firewall can’t filter (an Artifactory vnd.rt npm variant, a charset parameter, or a PyPI HTML simple index) now stream through unchanged instead of returning an error.Released: 2026-06-03
Released: 2026-06-02
Accept header, User-Agent prefix, and URL path pattern. Requests that can’t be classified stream through unchanged..tgz), wheels/sdists (PyPI), and .nupkg files are blocked at 403 when a per-version deny rule applies, preventing bypass via hard-coded artifact URLs..jar, .pom, .war, .aar, .zip (including classifier variants).api_address config field. The admin API (/api/eval, /api/update, /api/mode) listens on this address only when set.system_test/ (Docker-based mock origins for npm, PyPI, and NuGet plus a long-lived dotnet runtime container) covering both deployment modes.storage configuration section for defining the path cloned git repositories should be stored at, as well as a size limit./api/eval response shape is now structured JSON: {action, rule_id, ruleset_id, reason}, plus an error field on deny only (containing a human-readable message that npm-style clients display)./fw/ prefix and are served on api_address only.deployment: defaults to standalone; the Supervisor sets integrated explicitly when running the firewall as a Virtual Registry add-on.pkg/nuget/e2e harness and its nuget-e2e Make target. NuGet coverage now lives in the shared system_test/ suite.Released: 2026-05-11
mode option to the Artifact Firewall configuration, which can change the behavior of the firewall. Includes normal (default), hide and report.effective_action field, which for modes other than normal can be different than the action field.debug.Released: 2026-04-21