Changelog
The previous version of this page was a 20-line template that predated essentially all of the functionality below. This reconstruction groups the project's git log --oneline --merges origin/main history by theme rather than listing every commit; PR numbers (#NN) link to https://github.com/vaam-store/image-resizer/pull/NN.
emgr doesn't cut dated, tagged releases yet - Cargo.toml's version has stayed 0.1.2 throughout everything below, and .github/workflows/build.yml has no release-tag trigger: every published Docker image is either a floating <flavor>-latest or a per-commit <flavor>-<sha> (see Docker deployment). Once real, tagged releases start, this file should switch to genuine dated version headings per Keep a Changelog; until then, everything below is "Unreleased" in that format's sense.
⚠️ Breaking changes for existing deployments
- Signed URLs are on by default (#27). A deployment that previously ran without
SIGNING_KEY/SIGNING_SALTnow refuses to start. Set both (hex-encoded), or setALLOW_UNSIGNED_REQUESTS=trueto keep the old, unsigned behavior. /metricsnow requires authentication onotelbuilds (#77). A deployment scraping/metricswithout configuringMETRICS_AUTH_TOKENnow refuses to start. Set it and point Prometheus's scrape config at it via itsauthorizationblock, or setALLOW_UNAUTHENTICATED_METRICS=true.- OpenAPI code generation is gone (#53).
openapi.yamland the generatedpackages/gen-servercrate no longer exist - the HTTP router is hand-written. The wire API is unchanged, but any tooling that depended on the generated crate or the spec file needs updating.make init, the codegen bootstrap that used to be a prerequisite for a fresh clone, is gone with it. - Resize types honour imgproxy's real semantics instead of always cropping (#59, #1). A request using a resize type other than
fillnow gets a different result than before.
Security
- SSRF-guarded source fetching: scheme validation, private/loopback/ link-local IP-range blocking (each with an explicit opt-in), a source allowlist (
ALLOWED_SOURCES), and re-validation on every redirect hop - not just the original URL (#21). ALLOWED_SOURCESfixed to actually authorise the private origins it lists, instead of being silently overridden by the private-range guard (#57).- HMAC-SHA256 signed URLs, imgproxy-compatible, on by default (#27).
- Cache-key validation shared by every storage backend, closing an arbitrary-file-read via an unvalidated key (traversal, absolute paths, percent-decoded forms) - see
tests/storage_key_validation.rs(#23). - Resolution and output-size limits (
MAX_SRC_RESOLUTION_MP,MAX_OUTPUT_WIDTH/MAX_OUTPUT_HEIGHT,MAX_ANIMATION_FRAMES) - guards against decode bombs and many-tiny-frame animation bombs (#26). /metricsbearer-token authentication, fail-closed at startup onotelbuilds, mirroring the signed-URL check (#77).cargo-denywired into CI: RUSTSEC advisory scanning, license checks, duplicate/banned-crate checks, source-registry restriction (epic #9).- Alpha-channel compositing and transparent-pixel normalization fixed for target formats without alpha support (#34, #60).
- Base container images (Rust builder, distroless runtime) pinned by digest rather than a floating tag (#48).
Performance
- SIMD image resampling via
fast_image_resize, replacing theimagecrate's scalar resampler (#63stage 1). - DCT-scaled JPEG decode via mozjpeg/libjpeg-turbo: decodes close to the requested output size directly, instead of decoding full-size and downsampling afterward (
#63stage 2). - Full-size JPEG decode also routed through mozjpeg - measured ~1.5x faster than the
imagecrate's own decoder even without DCT scaling (#67). - JPEG encoding cut over to mozjpeg/libjpeg-turbo's
Compress, replacing theimagecrate's baseline encoder, alongside progressive-JPEG and chroma-subsampling controls (#76). - CPU-bound image processing moved onto
tokio::spawn_blocking, keeping the async runtime responsive under load; a configurable performance profile system (PERFORMANCE_PROFILE: high-throughput / low-latency / memory-efficient) and concurrency limits (epic #9). - Cgroup-aware CPU-count detection for Tokio worker-thread and concurrency sizing, instead of trusting the host's full core count from inside a resource-limited container (#44).
- A criterion micro-benchmark suite (
benches/) plus a three-way end-to-end harness against imgproxy (bench-imgproxy/), wired into a CI regression gate with a 15% threshold and a PR comment report (#20).
Formats and processing options
- Lossy and lossless WebP output, plus AVIF output (#35, #4, #49).
- Animated GIF/WebP output, with a configurable frame-count cap (#49).
- EXIF auto-orientation applied and ICC colour profiles forwarded to the output (#33, #5).
- Per-format quality control (#35).
- Progressive JPEG and chroma-subsampling toggles, plus a
max_bytesoutput-size cap (#76). - imgproxy-compatible resize types (
fill,fit, and the rest) instead of always cropping regardless of the requested type (#59). - Gravity, crop and geometry options; watermarks; named presets (
PRESETS) and a processing-option allowlist (ALLOWED_PROCESSING_OPTIONS) (#49, #50, #51, #52).
Architecture and reliability
- OpenAPI code generation removed: the HTTP router (
src/modules/api,src/modules/router,src/modules/url) is hand-written now, so a fresh clone builds with plaincargo buildand no Docker/codegen step (#53). - Cache lifecycle grew TTL support and reachable stale/evicted states, and local-filesystem writes became atomic (a directory could previously be mistaken for a cache hit, and a partial write could be served) - see
tests/storage_local_fs_atomicity.rs(#38, #40). - Graceful shutdown: SIGTERM/SIGINT drain in-flight requests before exit, and OpenTelemetry providers flush on exit instead of losing buffered telemetry on every restart (#42).
- Docker images are now actually run-tested in CI before being pushed, after the
s3/s3_otelimages shipped completely unable to start for a period (a glibc mismatch between the Rust builder and the distroless runtime - see Docker deployment) (#62).
Tooling and CI
- A real CI pipeline: a per-feature-set test matrix (
local_fs,s3,local_fs,otel), Clippy,cargo-deny, and the benchmark regression gate above - previously only a Docker build and a linter ran at all (epic #9, #46). - A docs/env-var drift check (
.github/scripts/check_env_docs.py) fails CI ifsrc/modules/env/env.rsand Configuration disagree in either direction (#47). - A Knative Serverless Helm chart (
helm/serverless/) added alongside the Deployment-based chart (helm/emgr/), for scale-to-zero setups. - A
PodDisruptionBudgetand liveness/readiness/startup probes added to the Deployment Helm chart - previously a voluntary disruption could evict every replica at once, and a wedged container was never restarted (#48). - ADRs recording the image-engine choice, the URL/API shape, and measurement writeups for the WebP and AVIF encoder decisions (
adr/0001throughadr/0005). Noteadr/0004is superseded byadr/0005, which re-measured AVIF against the encoders actually shipped (libavif/AOM and mozjpeg) after both sides of0004's comparison were replaced;0004is kept for the record, with its numbers marked void.