Skip to main content

13 posts tagged with "release"

View All Tags

Snakeway v0.15.0

Highlights

WASM devices

Snakeway now supports extending the proxy with WASM devices: custom traffic logic written in any language that compiles to a WebAssembly component.

Devices run in a sandboxed Wasmtime runtime (component model, snakeway:device@0.4.0) with access to request and response context, a host API for logging and metrics, and a typed configuration map. A bug in a device cannot crash the proxy, and each device runs in a memory-limited sandbox.

Devices are declared in a device file under device.d/:

wasm_devices = [
{
name = "my-device"
enable = true
path = "/etc/snakeway/devices/my_device.wasm"
fail_policy = "open" # "open" or "closed" on trap, timeout, or load error
timeout_ms = 5 # per-hook epoch deadline
body_buffer_max = 0 # 0 = streaming; > 0 buffers up to N bytes
hooks = ["on_request"]

config = {
api_key = "secret-value"
}
}
]

Hooks can inspect and modify request and response headers and bodies.

A reference JWT auth device ships alongside the runtime as a worked example, and it models secure identity handling for anyone adapting it. It rejects tokens that are missing required claims or signed with a secret that is too short, and it returns a generic error rather than revealing which check failed. It also strips client-supplied identity headers such as x-user-id and x-tenant-id before a request is forwarded, so a caller cannot spoof identity, including on public paths that bypass authentication.

caution

The WIT interface (snakeway:device@0.4.0) may change in future releases.

Engine-wide resource limits for the shared WASM runtime are set with the optional wasm block in server. max_concurrent_executions caps how many device hooks run at once, and max_memory_bytes caps the memory a single execution may use. Both default to Snakeway's previous built-in values, so existing configurations are unaffected. See the Server block for the field reference.

See Authoring WASM Devices for the full guide.

HTTP/2 server tuning

Listeners with enable_http2 = true accept an optional http2 block that tunes the HTTP/2 server parameters advertised to clients. Every field is optional and falls back to its default when unset.

bind = {
interface = "0.0.0.0"
port = 443
enable_http2 = true

http2 = {
max_concurrent_streams = 100
max_header_list_size = 65536
initial_window_size = 65535
initial_connection_window_size = 1048576
}
}

See HTTP/2 settings for the field reference.

Upstream connection tuning and reuse fix

This release remediates an upstream connection reuse issue and adds explicit control over upstream connection behavior through the new upstream block (see Breaking Changes for the relocation of existing fields).

Two new timeout settings are available:

  • connection_timeout_seconds bounds how long a connect (TCP plus the TLS handshake) may take before failing.
  • read_timeout_seconds bounds a stalled or wedged origin without breaking slow-but-progressing or streaming responses; the timer resets on every read and is not applied to websocket upgrades.

Both are optional and disabled when omitted, preserving v0.14.0 behavior.

upstream {
connection_pool_size = 256
connection_timeout_seconds = 10
read_timeout_seconds = 60
}

See the Server block for the full field reference.

Inspecting effective configuration

snakeway config dump gained a populated-spec representation. Run snakeway config dump --repr populated-spec to print your configuration with every optional block and field filled in with its default value. This shows the effective settings the proxy will apply without starting it, which is useful for confirming what a partial config actually resolves to.

The output format is selectable with --format hcl, --format json, or --format yaml.

snakeway config dump --repr populated-spec --format hcl

Breaking Changes

Upstream tuning consolidated into a single upstream block

Upstream connection settings are now grouped under one upstream block inside server. Two settings that lived elsewhere in v0.14.0 have moved into it:

  • The top-level upstream_source_addresses block is now upstream.source_addresses.
  • The performance.upstream_connection_pool_size field is now upstream.connection_pool_size.

The upstream block also introduces connection_timeout_seconds and read_timeout_seconds, which did not exist in v0.14.0.

Before (v0.14.0):

server {
performance {
work_stealing = true
upstream_connection_pool_size = 128
}

upstream_source_addresses {
ipv4 = ["10.0.1.5"]
ipv6 = ["fd00::1"]
}
}

After (v0.15.0):

server {
performance {
work_stealing = true
}

upstream {
connection_pool_size = 128
connection_timeout_seconds = 10 # new, omit to disable, but you probably want this
read_timeout_seconds = 60 # new, omit to disable, but you probably want this

source_addresses {
ipv4 = ["10.0.1.5"]
ipv6 = ["fd00::1"]
}
}
}

A config that still uses upstream_source_addresses or performance.upstream_connection_pool_size is rejected at load time, and the proxy refuses to start until it is updated.

What to do: move upstream_connection_pool_size out of performance into the new upstream block as connection_pool_size, and move the upstream_source_addresses block under upstream as source_addresses. No values need to change.

Upgrade Notes

Run snakeway config check before upgrading. If your v0.14.0 config sets upstream_source_addresses or performance.upstream_connection_pool_size, relocate them into the new upstream block as described above. The new upstream.connection_timeout_seconds and upstream.read_timeout_seconds fields are optional. Omit them to preserve the previous behavior of no upstream timeouts.

Internals / Developer

These changes do not affect configuration or runtime behavior. They are noted for contributors and anyone tracking the project internals.

confval matured into a standalone library

Reusable validation and conversion logic moved out of the application crates and into the confval configuration library.

  • A new keyword pipeline module adds KeywordSet for validating keyword fields, now used across the device and ingress specifications.
  • A new narrow pipeline module centralizes numeric and duration conversions that were previously scattered helper functions.
  • The derive macro gained support for #[confval(nested, default)] on non-optional nested fields. The attribute is now rejected on optional fields, enforced by a compile-fail test.
  • New reuse helpers parse_cidr_list and require_existing_file (the latter with remediation help) replace duplicated validation across CIDR and filesystem checks.

confval and confval-derive also received READMEs, TOML and HCL usage examples, and an enhanced prelude. The crates were published as confval 0.3.1 and confval-derive 0.1.1. A just doc task was added for building API documentation.

Snakeway v0.15.1

Highlights

Bug fix: HTTP/2 to HTTP/1.1 Host Header Missing

This is a protocol correctness issue.

Snakeway allows HTTP/2 ingresses to proxy to HTTP/1.1 upstreams. Strict upstreams (such as other proxies like nginx) require a Host header for HTTP/1.1 requests. A client may not send the Host header when submitting HTTP/2 requests. The fix in this release is to derive the Host header from the HTTP/2 :authority pseudo header.

Snakeway v0.14.0

Highlights

Source-level config diagnostics

Configuration errors now point to the exact file, line, and column where the problem is. Previously, validation errors were plain-text messages that named the offending field but left it to the operator to locate it. In v0.14.0, every error and warning includes source context so the problem is immediately visible.

Pretty output (snakeway config check):

error: invalid CIDR range
--> ingress.d/api.hcl:3:11
|
3 | allow = ["10.0.0.0/8", "bad"]
| ^^^^^
= help: expected a valid CIDR range (e.g. 10.0.0.0/8 or ::1/128)

Plain output (snakeway config check --format plain):

ingress.d/api.hcl:3:11: error: invalid CIDR range
help: expected a valid CIDR range (e.g. 10.0.0.0/8 or ::1/128)

JSON output (snakeway config check --format json) also includes source locations, making it straightforward to integrate with editors or CI tooling.

When multiple files contain errors, all of them are reported in a single pass rather than stopping at the first failure.

Pingora 0.8.1

The underlying proxy engine has been updated to Pingora 0.8.1. No configuration changes are required. This update includes upstream bug fixes and performance improvements.

Breaking Changes

Two identity-device checks that were warnings in v0.13 are now hard validation errors. A config that triggers either one no longer loads: snakeway config check reports the problem and exits non-zero, and the proxy refuses to start until it is fixed.

Each check catches a configuration that enables a feature the proxy cannot actually deliver, thus it is a misconfiguration. Promoting them to errors surfaces the mistake at config time. Leaving it as a warning would let the proxy start in a state where the feature silently did nothing, which is harder to notice than a failed config check.

If neither of these applies to your config, nothing changes for you.

Enabling GeoIP with no database now fails validation

The identity_device block accepts enable_geoip = true, but GeoIP enrichment needs at least one database to read from: geoip_city_db, geoip_isp_db, or geoip_connection_type_db. Enabling GeoIP without any of them is a no-op.

Before (v0.13): the config below loaded with a warning, and the proxy ran with GeoIP enabled but doing nothing.

After (v0.14.0): the same config is rejected.

# device.d/identity.hcl
identity_device {
enable = true
enable_geoip = true
# no geoip_*_db set
}
error: geoip enabled with no dbs specified
help: At least one geoip db must be specified

What to do: point at least one of geoip_city_db, geoip_isp_db, or geoip_connection_type_db at a database file, or set enable_geoip = false.

An unrecognized ua-parser regexes file now fails validation

When ua_parser_regexes points at a custom regexes file, Snakeway checks that the file looks like a uap-core regexes.yaml by looking for a user_agent_parsers section. A file without it is almost certainly the wrong file, and using it silently degrades user-agent parsing.

Before (v0.14.0): an unrecognized file produced a warning, and the proxy started using it anyway.

After (v0.14.0): the same config is rejected.

# device.d/identity.hcl
identity_device {
enable = true
ua_parser_regexes = "regexes.yaml"
}
error: ua_parser_regexes file does not appear to be a valid ua-parser regexes.yaml: regexes.yaml
help: Expected the file to contain a 'user_agent_parsers' section. See https://github.com/ua-parser/uap-core for the expected format.

What to do: point ua_parser_regexes at a valid uap-core regexes.yaml (one that contains a user_agent_parsers section), or remove the setting to use the bundled default.

note

This check is a content heuristic: it looks for the user_agent_parsers string, not a full YAML parse. An unusual but valid file that does not contain that literal section name will be rejected. If you hit that, removing the setting falls back to the bundled default.

Upgrade Notes

Run snakeway config check before upgrading. The two new validation errors described above will surface any affected configurations. If the check passes on v0.13.1, the same config will load without issues on v0.14.0.

Snakeway v0.13.1

Highlights

HTTP/2 to HTTP/1.1 protocol translation

Snakeway no longer requires TLS-enabled upstreams when serving HTTP/2 clients. Previously, HTTP/2 downstream connections were only supported for gRPC routes with TLS backends.

Now, HTTP/2 clients can connect to any TLS ingress bind and the proxy translates requests to HTTP/1.1 for plaintext upstream services automatically.

This means you can enable enable_http2 = true on an ingress bind and point it at standard HTTP/1.1 backends without any additional configuration. The proxy handles the protocol difference transparently.

Three protocol paths are now supported:

Client protocolUpstreamBehavior
HTTP/2 + TLSTLSEnd-to-end HTTP/2 (gRPC, h2-to-h2)
HTTP/2 + TLSPlaintextHTTP/2 to HTTP/1.1 translation (new)
HTTP/1.1EitherHTTP/1.1 pass-through (unchanged)

Response device pipeline for HTTP/2

Response devices (logging, header manipulation) now run on all HTTP/2 responses, not just HTTP/1.1 responses. Previously, the response device pipeline was skipped for HTTP/2 connections. This was incorrect for the h2-to-h1 path where the response is a normal HTTP response that should be processed by devices.

Upgrade Notes

No configuration changes are required.

Existing enable_http2 = true ingresses will continue to work as before for gRPC and TLS upstreams. The new h2-to-h1 translation activates automatically when an HTTP/2 client connects to an ingress that routes to plaintext upstreams.

Snakeway v0.13.0

Highlights

Graceful shutdown controls

Snakeway now gives you explicit control over how the proxy shuts down. When the process receives SIGTERM (or systemctl stop), active connections get a configurable window to finish before being dropped:

server {
shutdown {
drain_seconds = 10 # default: 10
force_timeout_seconds = 30
}
}

Previously, a shutdown with long-lived connections (WebSocket, gRPC streams) could hang indefinitely. The new defaults give connections 10 seconds to complete, and force_timeout_seconds provides a hard ceiling when set.

Organized server configuration (breaking)

Several server settings have been reorganized into logical groups. This makes the config file easier to read and reduces clutter at the top level of the server block.

Before (v0.12.0):

server {
version = 1
threads = 8
work_stealing = true
upgrade_sock = "/var/run/snakeway_upgrade.sock"
}

After (v0.13.0):

server {
version = 1
threads = 8

upgrade {
sock = "/var/run/snakeway_upgrade.sock"
}

performance {
work_stealing = true
}
}

The following fields moved into sub-blocks:

  • work_stealing moved into performance {}
  • upgrade_sock and upgrade_max_retries moved into upgrade {} (renamed to sock and max_retries)

All sub-blocks are optional. When omitted, the defaults apply.

New performance tuning options

Two new settings in the performance {} block let you tune connection handling:

performance {
upstream_connection_pool_size = 256 # default: 128
parallel_accepts_per_listener = 4 # default: 1
}

upstream_connection_pool_size controls how many idle connections are kept warm to upstream servers per worker thread. Increase this for high-traffic deployments with many backends.

parallel_accepts_per_listener controls how many accept tasks run per listener. Higher values reduce contention under bursty connection rates. Most deployments do not need to change this.

Upstream source address binding

A new upstream_source_addresses block lets you pin outbound upstream connections to specific local IP addresses:

server {
upstream_source_addresses {
ipv4 = ["10.0.1.5", "10.0.1.6"]
ipv6 = ["fd00::1"]
}
}

This is useful when your server has multiple network interfaces and you need upstream traffic to exit through a specific one, or when upstream servers use IP-based access control. When multiple addresses are specified, Snakeway round-robins across them.

Default config directory

The default config directory is now /etc/snakeway on all platforms. Previously, the --config flag was required unless SNAKEWAY_CONFIG was set. This aligns with the systemd unit and Docker image, both of which already used /etc/snakeway.

Improved systemd service

The packaged systemd unit gained several fixes discovered during production testing:

  • SNAKEWAY_LOG_DIR is now set, so logs are written to /var/log/snakeway/.
  • LogsDirectory=snakeway ensures the log directory exists with correct ownership.
  • TimeoutStopSec=30 prevents the stop command from hanging indefinitely if connections do not drain within the configured shutdown window.

Validation warnings no longer block startup

Configuration warnings (non-critical issues like unused TLS automation with no TLS listeners) no longer prevent the proxy from starting. Only errors block startup. Warnings are still printed to stderr so operators can address them at their convenience.

Documentation

  • The Server block reference documents all new settings: shutdown, performance, upstream_source_addresses, and the reorganized upgrade block.
  • New confval page documents the validation primitives crate.
  • The Configuration Internals page reflects the updated validation architecture.

Upgrade Notes

The work_stealing, upgrade_sock, and upgrade_max_retries fields have moved into sub-blocks. Update your snakeway.hcl before upgrading:

  • work_stealing = true becomes performance { work_stealing = true }
  • upgrade_sock = "..." becomes upgrade { sock = "..." }
  • upgrade_max_retries = 5 becomes upgrade { max_retries = 5 }

If you omit these blocks entirely, the defaults apply and no action is needed.

Snakeway v0.12.0

Highlights

Zero-drop hot reload of listeners

Snakeway can now reload listener-level configuration without dropping connections. Previously, changes to listener address, port, TLS termination, HTTP/2, connection filters, or worker thread count required a full restart with a brief interruption. These now apply through a zero-drop upgrade that preserves the TCP accept queue and lets in-flight requests drain to completion.

The reload loop picks the right path automatically. SIGHUP and POST /admin/reload continue to work for both runtime-swappable changes (routes, services, devices, TLS certs) and listener-level changes; operators do not need to choose between them.

A new snakeway upgrade command is available for manual control, but most operators will not need it. See the Hot Reload page for details, including the Linux-only caveat for the underlying file-descriptor transfer.

Admin API authentication (breaking)

The Admin API now requires bearer token authentication. Every admin request must present an Authorization: Bearer <token> header; requests without one (or with an unknown token) receive 401 Unauthorized.

Tokens are configured via a token file referenced from the bind_admin block:

bind_admin = {
interface = "127.0.0.1"
port = 8440
tls = {
mode = "manual"
cert = "/etc/snakeway/admin.crt"
key = "/etc/snakeway/admin.key"
}
auth = {
bearer = {
token_file = "/etc/snakeway/admin.tokens"
}
}
}

The token file holds one token per line, each at least 32 bytes. Multiple tokens are accepted concurrently to support rotation: append the new token, reload, migrate callers, remove the old token, reload again. There is no window where a caller must choose between the old and new token.

Authentication is the innermost of three layers and does not replace network-level restriction or TLS. The bind_admin listener still rejects wildcard interfaces and still requires manual TLS. See the Admin API guide for the full reference and rotation workflow.

Configurable config directory via environment variable

A new SNAKEWAY_CONFIG environment variable sets the config directory for all CLI commands. This removes the need to repeat --config /etc/snakeway on every invocation when working with a non-default config path.

export SNAKEWAY_CONFIG=/etc/snakeway

snakeway config check
snakeway config dump
snakeway route solve ...
snakeway run

An explicit --config flag always takes precedence. The packaged systemd unit and Docker image both set this variable to /etc/snakeway, so operators who SSH into a production host can run diagnostic commands without specifying the path.

Secret zeroize-on-drop

Bearer token digests and TLS private keys are now zeroed in memory when dropped, narrowing the window during which a process memory dump could expose long-lived secrets.

Everything else

CLI

  • New snakeway upgrade command for manually triggering a zero-drop upgrade. Like reload, it requires a configured pid_file. The automatic upgrade path triggered by reload is sufficient for most workflows.
  • New --test flag on snakeway run validates the configuration and exits with code 0 if valid or 1 if not. Useful for verifying a new binary or config before committing to an upgrade.

Server block

Two new fields support the zero-drop upgrade path:

  • upgrade_sock (default /tmp/pingora_upgrade.sock): set a unique value when running multiple Snakeway instances on the same host.
  • upgrade_max_retries (default 5): retry budget for the cross-process handoff.

Packaging

  • The Docker image and systemd unit both set SNAKEWAY_CONFIG=/etc/snakeway.

Documentation

  • New Hot Reload page covering both reload paths and platform constraints.
  • The Admin API guide gained a full Authentication section.
  • The Server block reference documents the new upgrade_sock and upgrade_max_retries fields.
  • The CLI reference documents the new upgrade command, the run --test flag, and the SNAKEWAY_CONFIG environment variable.
  • Added an Ubuntu/Debian install note pointing to the .deb package on the releases page.
  • Roadmap milestones reorganized.

Upgrade Notes

If you have a bind_admin block in your config, add an auth.bearer.token_file entry and create the token file before upgrading. Existing configs without admin auth will fail validation on the new release. See the Admin API authentication guide for the token file format.

If you intend to use the zero-drop upgrade path, ensure pid_file is set in your server block, and set upgrade_sock to a host-unique path if you run multiple Snakeway instances on the same machine.

Snakeway v0.10.0

Highlights

Observability

  • OpenTelemetry support fleshed out
  • Request tracing with span context propagation
  • Async telemetry initialization

Configuration overhaul (snakeway-conf)

  • Deferred upstream DNS resolution to connection time instead of startup
  • ACME directories now require pre-provisioning (no auto-creation)
  • Reworked device config loading, validation, and lowering
  • Eliminated duplicate validation

WASM device API

  • on_stream_request_body hook added with BodyChunk/BodyResult types
  • wit-bindgen upgraded, WIT bindings updated

Everything else

Repository restructuring

  • Moved source code into a crates/ workspace layout: snakeway, snakeway-conf, snakeway-core, snakeway-wit, snakeway-tests
  • Moved integration tests from tests/integration/ to crates/snakeway-tests/
  • Adopted workspace-level package metadata in Cargo.toml

Core library buildout (snakeway-core)

  • All business logic extracted/moved into snakeway-core
  • New runtime state management, server module, and types
  • Device pipeline rework: device trait extracted, explicit re-exports, WASM device improvements
  • Identity device added
  • Device path scoping (devices can now be scoped to specific URL paths)
  • ServiceId optimized from String to Arc<str>
  • Hotpath profiling instrumentation added
  • CL.TE smuggling check moved to early_request_filter
  • Removed redundant header lowercasing (HTTP library already canonicalizes)

Testing and CI

  • Fleshed out integration test suite with HTTP replay tests (raw .http fixture files over TCP)
  • Refactored ConfigBuilder for integration tests
  • Added Criterion microbenchmarks in snakeway-core/benches/ with a public bench_api module
  • Added sccache to CI, test count badges, coverage badges
  • Multi-platform build/release workflow (x86_64 + aarch64 musl)
  • Nextest configuration added

Dependency upgrades

  • wasmtime/wasi 42 -> 43, wit-bindgen 0.50 -> 0.56
  • criterion 0.5 -> 0.8, rand 0.9 -> 0.10, sha2 0.10 -> 0.11
  • tokio 1.48 -> 1.52, reqwest -> 0.13, tokio-tungstenite -> 0.29

Packaging and distribution

  • Dockerfile added
  • .dockerignore added
  • Packaging/distribution infrastructure (Phase 6)

Documentation and tooling

  • CLAUDE.md and 8 skill files added
  • Docusaurus docs resynced, versioning enabled
  • Justfile significantly expanded (+343 lines), decomposed into modular recipes
  • LLM_DISCLOSURE.md added
  • Public API renamed (run_cli -> run)

Snakeway v0.9.0

Breaking Changes

TLS configuration now requires a mode

The tls block inside bind and bind_admin now requires an explicit mode field.

Before (v0.8.0):

bind = {
tls = {
cert = "/path/to/certs/server.pem"
key = "/path/to/certs/server.key"
}
}

After (v0.9.0):

bind = {
tls = {
mode = "manual"
cert = "/path/to/certs/server.pem"
key = "/path/to/certs/server.key"
}
}

Set mode = "manual" to preserve the existing behavior. The new "acme" mode enables automatic certificate issuance and renewal.

Routes now require a hosts field

Service routes and static file routes now require a hosts list. This enables virtual hosting — multiple domains can be served from a single Snakeway instance.

Before (v0.8.0):

routes = [
{ path = "/api" }
]

After (v0.9.0):

routes = [
{
hosts = ["example.com"]
path = "/api"
}
]

Use ["*"] to match all hostnames and preserve previous behavior when upgrading:

routes = [
{
hosts = ["*"]
path = "/api"
}
]

New Features

Automatic TLS Certificate Renewal (ACME)

Snakeway now supports automatic TLS certificate issuance and renewal via the ACME protocol (Let's Encrypt).

Configure tls_automation in snakeway.hcl:

server {
tls_automation = {
renew_within_days = 30
acme = {
directory_url = "https://acme-v02.api.letsencrypt.org/directory"
data_dir = "/var/lib/snakeway/acme"
contact_email = ["admin@example.com"]
}
cert_store = {
type = "filesystem"
cert_dir = "/var/lib/snakeway/acme/certs"
}
}
}

Then set mode = "acme" on any bind block you want managed automatically:

bind = {
tls = {
mode = "acme"
domains = ["example.com", "api.example.com"]
challenge = "http01"
}
}

Certificates are renewed automatically in the background. No restart or reload is required.

See the TLS Cert Management guide for full details.

Virtual Hosting (SNI / host-based routing)

Routes now accept a hosts list, allowing a single Snakeway instance to serve multiple domains. Incoming requests are matched against the Host header before path matching is applied.

See the Routes reference for full details.

Upstream TLS

Upstream connections can now be made over TLS. Configure this per endpoint:

endpoint = {
host = "backend.internal"
port = 8443
tls = {
sni = "backend.internal"
verify = true
// ca_file = "/path/to/ca.pem" // optional; falls back to server.ca_file
}
}

See the Upstream TLS reference for the full field reference.

route solve CLI Command

Debug routing decisions without starting the proxy. The command runs the same config loading, lowering, and routing logic used by the live proxy:

snakeway route solve http://example.com/api/v1/users --config /etc/snakeway

Supports --trace, --verbose, --format=json, and deterministic upstream selection via --lb-index / --lb-key.

See route solve for full documentation.