All notable changes to vmod-wasm will be documented in this file.
- Added a documentation guide that gives operators and module authors a clearer reading path through production, development, configuration, compatibility, architecture, and security docs.
- Included documentation media assets and all example README files in the Automake source distribution.
- Added a Docker-backed
make perf-testharness that compares baseline proxying, raw Wasm execution, Proxy-Wasm header callbacks, response-body inspection, and response-body rewrite paths.
- Clarified documentation around the HTTP-focused Proxy-Wasm ABI surface and Varnish-specific unsupported features.
- Disabled store pooling for modules that export
_initialize, including the Rust Proxy-Wasm SDK, because memory-only pool resets cannot safely restore mutable Wasm VM state.
- Aligned Proxy-Wasm map and buffer enum values with ABI v0.2.1, including HTTP call response maps and foreign-function argument buffers.
- Implemented response-body replacement semantics for
proxy_set_buffer_bytesand forwarded rewritten body chunks through the Varnish delivery processor. - Reported serialized header map sizes from
proxy_get_header_map_sizeand returned explicit status codes for unsupported gRPC, stream, and foreign surfaces. - Fixed the SDK response-body example and VTC coverage so final response body chunks can be rewritten correctly.
- Refactored release, production, development, architecture, compatibility, and security documentation around the Varnish 9 release contract.
- Documented current runtime defaults, per-VCL engine lifetime, bundled Wasmtime install expectations, and soak-test workflow.
- Enforced
wasm.set_fail_mode("open")in VMOD execution return paths so documented fail-open behavior returns0on Wasm execution errors.
- Isolated Wasm engines per loaded VCL so VCL reloads and discards no longer tear down modules still owned by another active VCL.
- Reordered engine shutdown so tick timers, store pools, warm instances, and HTTP pools are destroyed before Wasmtime modules and the Wasmtime engine.
- Hardened HTTP connection pooling by applying socket I/O timeouts and refusing to reuse pooled sockets with unread stale data.
- Initialized Proxy-Wasm warm lifecycle calls with a real host context, memory, allocator, shared data, queue store, and metric store.
- Copied Proxy-Wasm header and request-property mutations into Varnish workspace before passing them to Varnish HTTP APIs.
- Fixed Proxy-Wasm header-map size reporting for request and response pseudo headers.
- Rejected duplicate module names in a VCL-local engine and cleaned up failed module loads more defensively.
- Added VTC coverage for VCL reload lifecycle isolation.
- Hardened
proxy_http_callrequest parsing by rejecting malformed serialized header maps, invalid methods/paths, bad upstream ports, and CRLF/control injection in guest-supplied headers. - Fixed a response-buffer boundary bug where a full 256 KiB callout response could write the trailing NUL byte one byte past the allocation.
- Switched outbound callout writes to retry until the complete request and body are sent, avoiding partial-write truncation.
- Fail closed for oversized, malformed, incomplete, or chunked callout responses instead of storing a partial response.
- Apply private/internal IP rejection consistently through both direct HTTP connections and the HTTP connection pool.
- Return pooled callout connections for reuse only after a complete,
Content-Lengthframed HTTP/1.1 response.
- Added VTC coverage for oversized auth callout responses failing closed with the HTTP connection pool enabled.
- Varnish 9 releases now use
varnish9-vX.Y.Ztags so compatibility is visible in the release identity, not only in asset names. - GitHub release titles now use
Varnish 9 / vX.Y.Zwhile the package version remains standard semantic versioning.
- Release artifacts are renamed with the Varnish channel first, for example
vmod-wasm-varnish9-4.3.1-linux-amd64.tar.gz. - Release notes now lead with the Varnish, Wasmtime, platform, and tag contract.
- Release manifests are named
manifest-varnish9-X.Y.Z.jsonand include the release tag and channel metadata.
- Avoids publishing Varnish-specific binaries under a generic
vX.Y.Ztag line.
- BSD-2-Clause open-source release metadata and community files
- Source distribution checks for release readiness
- GitHub release tarballs with bundled Wasmtime runtime, checksums, and manifest
- Public support target is Varnish 9.x with Wasmtime 44.0.0
- Version metadata synchronized across Autotools, VMOD runtime, Docker, and docs
- Documentation updated for epoch deadlines, INT return values, and release bundles
make distcheckcan build Wasm test fixtures from the source archive- Release workflow now includes all example
.wasmartifacts, includingtransform.wasm
- Removed dead C unit test files (693 lines) not wired into build
- Deduplicated README: consolidated docs links, removed inline code examples (199 → 137 lines)
- Multi-arch release builds: pre-compiled
.sofor linux/amd64 and linux/arm64 - All example
.wasmmodules attached as portable release assets
- Multi-call HTTP response storage: Replaced single HTTP call response state
with a Varnish VRBT (red-black tree) keyed by Proxy-Wasm
token_id, enabling modules to dispatch multipleproxy_http_callrequests per stream.
- Deferred callback delivery now iterates all pending call responses and
invokes
proxy_on_http_call_responsefor each token in order. - Header and buffer access for HTTP call responses now use the active call
entry context (
active_http_call) instead of a single global response slot.
- Removed response overwrite behavior when multiple callouts were dispatched in a single request lifecycle.
- Cleanup now frees all stored call response entries safely via VRBT traversal.
proxy_http_calldeferred callback:proxy_on_http_call_responseis now invoked afteron_http_request_headersreturns, avoiding the proxy-wasm SDKRefCellre-entrancy panic that occurred when the callback was called inline- Auth callout in edge-security-filter example: dispatches an HTTP call to a
configurable
auth_servicewhen anAuthorizationheader is present; sends401 Unauthorizedif the auth service returns a non-200 status - New VTC test
proxy_wasm_edge_filter_callout.vtc: covers valid auth (pass-through), invalid auth (401 synth), and noAuthorizationheader (pass-through without callout)
- Reset
actionfromPAUSEtoCONTINUEafter a successful callout so the request proceeds normally when the auth service approves - Propagate
local_response_code(e.g. 401) back to VCL via thestatus_codeoutput parameter ofproxy_wasm_on_request_configured - Restore
local_response_setcheck on the non-callout path (bot/geo blocking) that was accidentally removed during the deferred-callback refactor
- Production-ready WASI implementations:
fd_write: Full iovec parsing with bounds checking; stdout/stderr only; output goes to stderr for visibility in systemd journalclock_time_get: Real nanosecond-precision time viaclock_gettime(supports CLOCK_REALTIME and CLOCK_MONOTONIC)random_get: Cryptographically secure random bytes viagetrandom(2)(Linux),arc4random_buf(macOS/FreeBSD), or/dev/urandom(fallback)
- Filter chain test modules: Dedicated
passthrough.wasmandtransform.wasmfor filter chain integration test coverage - New
vwasm_engine_get_pool()accessor for opaque store pool access
- VDP chain API updated for Varnish 7.6+ (
void **privcallback signatures) - VTC test deadlocks: fixed server start patterns in
filter_chain.vtcandpool_stats.vtc(removed double-start race condition) - Filter chain server repeat count corrected (4 clients =
-repeat 4) - Duplicate
*priv = NULLinvdp_wasm_finiremoved - Automake
subdir-objectswarning resolved
- Unit tests removed from build (cannot link standalone against Varnish internals); all testing via VTC integration tests (19 tests, full coverage)
- WASI stubs upgraded from no-ops to real implementations with proper WASI errno codes
- Proxy-Wasm ABI completeness: Implemented
proxy_get_header_map_size,proxy_get_buffer_status, andproxy_get_log_levelhost functions. - WASI stubs: Modules compiled with
wasm32-wasitarget now instantiate without error. Provides stubs forfd_write,clock_time_get,random_get,environ_sizes_get,environ_get,args_sizes_get,args_get, andproc_exit(traps instead of exiting). - Stream control functions (
proxy_continue_stream,proxy_close_stream) registered and documented.
- Removed internal scoping block in HTTP callout code (variables moved to function scope for clarity).
- Documentation overhaul: removed internal implementation plan, rewrote README as user guide, updated COMPATIBILITY matrix, replaced all "fuel" references with epoch-based terminology.
- Copyright year corrected to 2025 across all source files.
- Streaming VDP body processing: Response body is no longer buffered.
Each chunk is passed to
proxy_on_response_bodyimmediately as it arrives (withend_of_stream=0), then forwarded to the client. On the final chunk,end_of_stream=1is set. This eliminates the 1 MiB buffer entirely — memory usage for body inspection is now O(chunk_size) instead of O(body_size). - Epoch-based interruption: Replaced fuel-based instruction counting with epoch-based timeout protection. A background timer thread increments the engine epoch every 1ms. Each callback gets a configurable deadline (default 100ms). This reduces per-instruction overhead significantly while still preventing infinite loops.
wasm.set_epoch_deadline(ms): Configure the per-callback execution timeout in milliseconds (default: 100ms).wasm.set_http_timeout(ms): Configure the HTTP callout timeout forproxy_http_call(default: 5000ms, cap: 30s). Previously hardcoded.vwasm_engine_reset_epoch_deadline(): Internal API for extending the epoch deadline before each wasm callback phase.
- HTTP timeout now respects module-supplied timeout (if non-zero), falls back to engine-configured default, with a hard cap at 30 seconds.
- Response body access via VDP:
proxy_on_response_bodynow receives actual response body data via Varnish Delivery Processor pipeline. Body is buffered (up to 1 MiB) and passed to the callback on stream end. Activate withset resp.filters += "wasm_body"invcl_deliver. - HTTP call response callback:
proxy_on_http_call_responseis now invoked afterproxy_http_callcompletes, matching the Proxy-Wasm ABI spec's async callback pattern. Previously modules had to read response data directly viaproxy_get_buffer_bytes.
proxy_loglevel mapping:LogLevel::Infono longer incorrectly gets "WARN:" prefix (threshold changed from DEBUG to INFO).- Fuel exhaustion in VDP/HTTP callbacks: fuel is now refilled before each body phase, lifecycle callback, and HTTP call response callback.
- Anti-IP-rebinding:
proxy_http_callnow validates resolved IPs against RFC1918, RFC5735, RFC4193, and loopback ranges to prevent SSRF attacks via DNS rebinding. - proxy_set_property: Wasm modules can now mutate request properties:
request.path/request.url_path— URL rewritingrequest.method— HTTP method changerequest.host— Host header modification
- Request body access:
proxy_on_request_bodynow receives actual body data (up to 1 MiB, automatically cached via VRT_CacheReqBody). - Execution statistics: New
wasm.get_stats_json()VCL function returns atomic counters: calls_total, calls_ok, calls_error, calls_timeout, local_responses, http_calls, http_calls_blocked, body_bytes_in, fuel_total. - Execution timing: Logs VSL warning when Wasm execution exceeds 10ms.
- Structured logging: All proxy_log output prefixed with
[wasm:<module>].
- Merged 4 duplicated lifecycle functions into single generic
proxy_wasm_execute()(saved ~650 lines). - Consolidated 11 stub functions into 2 generic stubs (
pw_stub_ok,pw_stub_not_found). - Removed all
pthread_rwlock— config is immutable aftervcl_init. - Trimmed VCC documentation from 270 to 117 lines.
- Consolidated test suite from 18 to 16 test files.
- Updated COMPATIBILITY.md to reflect actual implementation coverage.
proxy_set_header_map_pairswas documented as stub but was actually implemented — fixed documentation.
- Use
AN()andCHECK_OBJ_NOTNULL()per Varnish idioms for all context pointer assertions - Add
$Restrictdirectives to VCC for compile-time VCL method enforcement:load(),set_fuel(),set_memory_limit(): restricted tovcl_initexecute(),proxy_wasm_on_request(): restricted toclientscope
First stable release.
- Upgraded to Varnish 8.0 and Wasmtime 44
- Fixed
hdr_tcompatibility for Varnish 8 API
wasm.load(name, path)— Load.wasmmodules at VCL initwasm.execute(module, func)— Call exported Wasm functions from VCLwasm.version()— Return VMOD version string- Wasmtime C API integration with engine/linker/store lifecycle
- Thread-safe module registry (read-write lock, up to 64 modules)
- 6 host functions under
envnamespace:get_request_header— Read any request headerget_request_url— Read the request URLget_request_method— Read the HTTP methodget_client_ip— Read the client IP addressset_response_header— Set a response headerlog_msg— Log messages to Varnish Shared Log (VSL)
wasm.set_fuel(fuel)— Configurable fuel (instruction) limitswasm.set_memory_limit(bytes)— Configurable memory limitswasm.get_fuel()/wasm.get_memory_limit()— Query current limits- Trap message extraction and logging to VSL
wasm.proxy_wasm_on_request(module)— Execute Proxy-Wasm filter lifecycle- 10 Proxy-Wasm host functions:
proxy_log— Log with Proxy-Wasm log levelsproxy_get_header_map_value— Read headers by nameproxy_add_header_map_value— Add/set headersproxy_replace_header_map_value— Replace header valuesproxy_remove_header_map_value— Remove headersproxy_get_property— Read request properties (path, method, protocol)proxy_send_local_response— Send immediate responses (e.g. 403)proxy_get_current_time_nanoseconds— Current timeproxy_set_effective_context— Context switching (stub)proxy_get_buffer_bytes— Buffer access (stub)
- Full Proxy-Wasm lifecycle: context_create → vm_start → configure → request_headers
- Memory allocator protocol via
proxy_on_memory_allocateexport
- Dockerfile with Debian bookworm-slim, Varnish 8.0, Wasmtime 44, Rust
- GitHub Actions CI pipeline
- autotools build system (automake/autoconf/libtool)
- 12 VTC tests