Repository navigation
Releases: hlindset/image_pipe
Releases · hlindset/image_pipe
Release list
v0.2.0
image_pipe_url
Removed
- Breaking:
ImagePipe.Plan.Source.Path,ImagePipe.Plan.Source.URL, and
ImagePipe.Plan.Source.Object. They are nowImagePipe.Source.Path,
ImagePipe.Source.URL, andImagePipe.Source.Objectinimage_pipe, whose
custom source adapters receive them. - The dependency on
mime. The package depends only onnimble_optionsand
colorat runtime.
image_pipe
Added
- SVG sources, when the optional
image_pipe_svgpackage is installed.
An SVG renders straight at the size the request needs, so it stays sharp
at any size, and a resize can make it larger than its ownwidthand
heightwithoutenlarge, up to the input pixel limit, past which the
rendered image is enlarged. Regions and crops in pixels measure the SVG at
its own size. With noformatand no negotiated format, the result is a
PNG. Watermark images can be SVGs too, and render at the size they're
drawn. Withoutimage_pipe_svg, SVG sources still fail with415. - The
:svgoption configures SVG sources::font_dirsand
:fallback_familyfor the fonts SVG text renders in (no system fonts are
used), and:max_bytes,:max_render_work, and:max_render_pixelsto
limit the work an SVG can cause. - A
[:source, :svg_render]span wraps rendering an SVG original or
watermark, with its intrinsic and rendered sizes and the estimated work,
and names the limit that rejected an SVG. The default Logger logs it. cacheandinput_cachedelete metadata files they can't read, such as
ones another ImagePipe version wrote, and their bodies, once the files are
more than an hour old. The[:cache, :sweep]event reports them as
:unreadable.[:cache, :stage]reports writes the cache drops to keep up with its
storage as:stage_dropped, for processed images and originals, and
carries:pool. The default Logger logs dropped writes at:warning.[:cache, :coordination]carriesreason: :deadlinewhen a request stops
waiting for another request's image because it wasn't stored in time.- The
image_pipe.requestspan carries arequest_idattribute when Logger
metadata has a:request_id, such as the onePlug.RequestIdsets. - The
[:cache, :lookup],[:cache, :write], and[:cache, :stage]
telemetry events carry:cache_key, the hash of the entry's cache key, and
[:cache, :lookup]carries:entry, which says whether it read a processed
response or a source record. The tracer adds them to their spans and span
events. - The
[:cache, :write]stop event carries:estimated_cost_us, the work
the cache estimates it took to make a processed image. The tracer adds it
to the span. - The
[:cache, :write]stop event carries:bytes, the size of the stored
body, so dividing it by the span's duration gives a rough write speed for the
cache storage.[:cache, :lookup]carries:byteson a hit for a processed
response. The tracer adds:bytesto both spans, and the default Logger
logs it. - A
[:source, :decode_open]span wraps each time libvips opens the original,
so a trace shows how often a request opens it. The default Logger doesn't log
it.
Changed
- With
image_pipe_svginstalled, cache keys and ETags include the resvg
version, the fonts, and the fallback family, so every cached image misses
once after installing it, and again after changing the fonts or fallback
family, or upgradingimage_pipe_svgto a version that renders SVGs
differently. - Breaking: An HTTP source's
allowed_hostsentry without a port allows
only the scheme's default port, 80 or 443, including for redirect targets.
Before, any port on a listed host was reachable. Add an entry with the port,
such as"assets.example.com:8443", for an origin on another port. A
base_urlwith a non-default port needs that port inallowed_hostswhen you
set the list. A malformed entry, such as"assets.example.com:https", now
fails at startup. - A response to a URL with
expiresdropsstale-while-revalidateand
stale-if-error, including ones the host sets, because caches can't use them
alongside itsmust-revalidate. - Processed images and originals cached by an earlier version are not
reused, because the cache key changed and entry files are named
differently. The cache refills as requests come in, and deletes the old
files as
leftover files. - Requests for PNG and other sources that need no shrink-on-load open the
source once instead of twice, which saves about 0.3 ms per request. - A custom detector's
identity/1,available?/1, andready?/1receive only
:classes, asdetect/2already did. They no longer see the mount's other
configuration. - Breaking: The source identifiers a custom source adapter receives are
ImagePipe.Source.Path,ImagePipe.Source.URL, andImagePipe.Source.Object.
ReplaceImagePipe.Plan.Source.Path,.URL, and.Objectin your adapter. - Breaking: A source's
internal_cachetakes:enabledor:disabled,
and defaults to:enabled. Replaceinternal_cache: :auto, which meant the
same as:enabled. - Breaking:
c:ImagePipe.Source.S3.CredentialProvider.fetch_credentials/2
takes the bucket and the provider's options. Change your provider's
fetch_credentials/3tofetch_credentials/2by dropping the third
argument, which was always[]. - Breaking:
cacheandinput_cachetake the file system cache's options
directly. Replacecache: {ImagePipe.Cache.FileSystem, root: "..."}with
cache: [root: "..."], and the same forinput_cache. The server's
[cache]configuration is unchanged. - Breaking: The tracer now creates OpenTelemetry spans as they happen, and
needs the OpenTelemetry SDK in your application. Remove theexporter:
option fromImagePipe.Telemetry.attach_tracer/1and the
ImagePipe.Telemetry.Trace.OtelIdGeneratorid_generatorsetting from your
:opentelemetryconfiguration. To print spans without a backend, use the
SDK's stdout exporter. Span and trace IDs now come from the SDK, and each
span reaches the SDK when it ends, not when the request finishes. - A request's spans join the trace of the span that is current when the
request starts, such as one from your Phoenix or Bandit instrumentation.
This includesImagePipe.run/4in an instrumented job. With
extract_inbound: true, the inboundtraceparentapplies only when no span
is current. - Spans your own code opens inside ImagePipe's processes, such as in a custom
source or detector, nest under the request's spans, with or without the
tracer attached. - A request that continues an inbound trace follows the caller's sampled
flag under the SDK's default parent-based sampler. It was always recorded
before. - Breaking:
[:cache, :eviction, :stop]reports the entries a write
evicts, withtrigger: :admission, and the startup scan's batches with
trigger: :startup. Update handlers that matchtrigger: :reconcile. - Responses no longer wait for the cache's disk. A processed image is stored
in the background after it's sent, and a downloaded original while the
request processes it. The[:cache, :write]span can end after the
request. - A request waiting for another request's image or original stops waiting
when the image or original isn't stored 2 seconds after it was sent or
downloaded. - When the disk can't keep up, writes are dropped, and unfinished writes
count againstmax_size_bytes. An entry larger than a twentieth of
max_size_bytes, or 256 MiB, is never stored (see
writing entries). trimis faster on images under a megapixel: 2 ms instead of 6.5 ms on a
256×256 image.- Breaking:
cacheandinput_cacheneedmax_size_bytes, and an
ImagePipe instance (seeImagePipe.child_spec/1). Addmax_size_bytesto
each cache, and to the options ofImagePipe.Cache.FileSystem.get/2, and
move a configuration with a cache fromImagePipe.Plugoptions or an
ImagePipe.run/4call into an instance.run/4with an instance name
can't setcacheorinput_cacheper call. - Breaking: Each
rootofcacheandinput_cacheserves one node. An
instance writes a.image-pipe-ownerfile into each cache'sroot.
While another node keeps that file updated, the instance logs an error and
runs without that cache, and it takes therootover once the other node
stops. Give each replica its own cache directories, such as its own volume.
When a node is killed, a node on another host that starts after the kill
takes over itsrootabout 30 seconds after starting. cacheandinput_cachekeep the entries that are requested often and
took the most work to make for their size. For a processed image, that work
is estimated from the request instead of timed (see
work to make an entry).
On startup a cache reads only file names, so a large cache is ready sooner.
Removed
- Breaking:
ImagePipe.Transform.Detector.Composite.new/1and
default/0. A detector is configured as a module, so a composite struct
could not be configured. ConfigureImagePipe.Transform.Detector.Composite
itself to get the default face and object detectors. - Breaking: Bounded caches keep request counts in memory only. Remove
node_id,state_dir,flush_interval,cleanup_interval, and
state_ttlfromcacheandinput_cache. After a restart, a bounded
cache keeps its entries and learns again which are requested most. You can
delete the.cache_statedirectory under each bounded cache'sroot. - The
[:cache, :warm_start],[:cache, :flush, :stop], and
[:cache, :cleanup, :stop]telemetry events. - Breaking: The
eviction_victim_limitand `reconcile_i...
image_pipe_svg-v0.1.0
Added
- Rendering SVG for ImagePipe with resvg, through a precompiled native
library. Rendering never reads files or opens connections, draws text only
in the fonts it's given, and accepts embedded PNG, JPEG, GIF, and WebP
images and SVGs nested two levels deep. It bounds how far entities and
<use>can expand, and how much text it lays out, before parsing, and
estimates the cost of a render before it renders, so ImagePipe can reject
an SVG that's too expensive.
image_pipe_server 0.2.0
Added
- The
IPS_CONFIG_TOMLenvironment variable holds the whole TOML
configuration, for platforms that can't mount a configuration file.IPS_
setting variables override it as they override the file. Setting both
IPS_CONFIGandIPS_CONFIG_TOMLis an error. - SVG originals and SVG watermarks render like other images. An SVG renders
straight at the size the request needs, so it stays sharp at any size, and
a resize can make it larger than its own size withoutenlarge. Without a
formator a negotiated one, the result is a PNG. [processing.svg]configures SVG rendering:font_dirsand
fallback_familyfor the fonts SVG text renders in (mount a font
directory into the container, since no system fonts are used), and
max_bytes,max_render_work, andmax_render_pixelsto limit the work
an SVG can cause. Afont_dirsentry that doesn't exist, or a file in it
that isn't a font, stops the server from starting.- Logs and traces have an
image_pipe.source.svg_renderspan for each SVG
rendered, with its own and rendered sizes, the estimated work, and the
limit that rejected an SVG. - Caches delete files they can't read, such as ones another ImagePipe
version wrote, once the files are more than an hour old. The
image_pipe.cache.sweepspan reports them asunreadable. - Cache lookup and write spans, and cache stage span events, carry a
cache_keyattribute, the hash of the entry's cache key. Lookup spans also
carryentry(responseorsource_record), and stage events carry
pool(outputorinput). - Cache write log lines and spans carry
bytes, the size of the stored
image, and lookups carry it on a hit for a processed image. Write spans
also carryestimated_cost_us, the work the cache estimates it took to
make the image. - The
image_pipe.requestspan carries arequest_idattribute, the ID
that tags the request's log lines. - Traces have an
image_pipe.source.decode_openspan for each time the
original is opened for decoding.
Changed
- Breaking:
[cache.output]and[cache.input]needmax_size_bytes.
A cache without it stops the server from starting with
cache.output.max_size_bytes: required. Setmax_size_byteson each
cache, below the free space on its volume. - Breaking: Each cache
rootserves one server. The server writes a
.image-pipe-ownerfile into each cache root. While another server keeps
that file updated, the server logs an error and runs without that cache,
and it takes the root over once the other server stops. Give each replica
its own cache volume. A server takes over a root left by a crashed server
on another host about 30 seconds after it starts. - Breaking: A source's
internal_cachetakes"enabled"or
"disabled", and defaults to"enabled". Replace
internal_cache = "auto", which meant the same as"enabled". - Breaking: An
allowed_hostsentry of anhttpsource allows only the
scheme's default port, 80 or 443, including for redirect targets. Add an
entry with the port, such as"assets.example.com:8443", for an origin on
another port. Abase_urlwith a non-default port needs that port in
allowed_hostswhen the list is set. A malformed entry, such as
"assets.example.com:https", stops the server from starting. - Breaking: The
image_pipe.cache.evictiontrace event and the cache
eviction log line reporttriggerasadmissionorstartupinstead ofreconcile. - Processed images and originals cached by an earlier version miss once,
because cache keys and file names changed. The cache refills as requests
come in and deletes the old files. Cache keys now also include the SVG
renderer and the fonts, so changing the fonts or the fallback family makes
cached images miss again. - The cache keeps the images that are requested often and took the most
work to make for their size. At startup it reads only file names, so a
large cache is ready sooner. - Responses no longer wait for the cache volume. A processed image is stored
in the background after it's sent, and a downloaded original while the
request processes it. A request waiting for another request's image or
original stops waiting when it isn't stored 2 seconds after it was sent or
downloaded, and theimage_pipe.cache.coordinationtrace event reports
reasonasdeadline. - When the cache volume can't keep up, writes are dropped instead of queued,
and unfinished writes count againstmax_size_bytes. A write evicts what
it needs before it's stored, so stored images stay under
max_size_bytes. An image larger than a twentieth ofmax_size_bytes, or
256 MiB, is never stored. Each dropped write logsstage_droppedat
warning. - A response to a URL with
expiresdropsstale-while-revalidateand
stale-if-error, because caches can't use them alongside its
must-revalidate. - Each trace span is exported when it ends, not when the request finishes.
- With
trust_traceparent, a request that carries atraceparentheader
follows the caller's sampled flag. Before, it was always exported. - The server uses about 140 MiB less memory: it loads its code when it first
needs it instead of all at startup. SetRELEASE_MODE=embeddedto load
everything at startup. - Before it reports ready, the server processes a small image in each output
format, so the first requests don't wait for the image-processing code to
load or an encoder to start./health/readynow answers503until then, including on the
separatehealth_portlistener. - Images process faster, PNG most of all. The Docker images now ship zlib-ng
2.3.3, libjpeg-turbo 3.2.0 and libpng 1.6.59 in place of Debian's zlib,
libjpeg-turbo and libpng. Across a set of typical requests, latency dropped
by about a quarter, and by more than half for some PNG resizes. - The server handles about 5–10% more images per second when the container
has a CPU limit: the Erlang VM's schedulers no longer spin while they wait
for work, which took CPU that image processing needed. trimis faster on images under a megapixel.- PNG originals, and others that need no shrink-on-load, are opened once
instead of twice. - Uses
image_pipe
0.2.0.
Removed
- Breaking:
node_id,state_dir,flush_interval,
cleanup_interval, andstate_ttlin[cache.output]and
[cache.input]. The cache keeps request counts in memory, so after a
restart it keeps its entries and learns again which are requested most.
Remove the settings, and delete each cache'sstate_dir, by default
.cache_stateunder its root. - Breaking:
eviction_victim_limitandreconcile_intervalin
[cache.output]and[cache.input]. Remove them from your configuration. - Breaking:
window_ratio,sketch_depth,sketch_width,
aging_sample_size,doorkeeper_cardinality,doorkeeper_fpr, and
path_prefixin[cache.output]and[cache.input]. Remove them from
your configuration. The cache no longer reads or cleans up the entries
under apath_prefixdirectory, so delete that directory. - Breaking:
rescan_intervalin[cache.output]and[cache.input],
and theimage_pipe.cache.rescanspan and log line. Remove
rescan_intervalfrom your configuration. A cache deletes leftover files
after its startup scan and then every 24 hours. - Breaking: The
image_pipe.cache.warm_startspan and the cache flush
and cleanup span events. Remove alerts or queries that use them. - Cache stage events no longer report
stage_cleanup_error.
Fixed
- A cache write log line and span report an error when the cache isn't
running, instead of reporting the image as written. - A comma inside a quoted
Acceptparameter, as in
image/webp;x="a,b";q=0, no longer splits the entry and drops theq=0,
so the server no longer chooses a format the client excluded. - With
[http] allow_originset, cross-origin scripts can send conditional
requests such asIf-None-Matchand read headers such asETagand
Content-Disposition. Preflight requests get
Access-Control-Allow-Headers: *, and responses get
Access-Control-Expose-Headers: *. - With
max-bytes, a quality search that ran out of encode attempts ships
the highest encoded quality that fits instead of the lowest. When only the
lowest quality fits,X-ImagePipe-AQ-Outcomeand the logs reporthit. - A
cropplaced withfocuson an image with an EXIF orientation other
than 1 or 5 could land one pixel off when its position fell on a half
pixel. - The source cache stores and reuses originals whose
Cache-Controlhas a
qualifiedprivate="..."orno-cache="..."naming only headers
ImagePipe doesn't keep, such asprivate="Set-Cookie". Before, they were
never stored, or revalidated on every request. - Concurrent fetches from an
httpsource just after startup could fail.
They now wait for the connection pool to start. - A CMYK or other non-RGB source with an embedded color profile and more
pixels thanmax_intermediate_pixelsfails with422, like other
oversized images, instead of415.
Built with image_pipe and image_pipe_url 0.2.0.
v0.1.0
image_pipe_url
Added
First release of the ImagePipe URL builder.
- Build typed processing plans and generate image URLs with explicit processing
groups and output options. - Validate processing options, reference named presets with request overrides,
and optionally check plans against the serving mount's presets. - Sign URLs, set expiry, and encrypt source references.
- Generate URLs in applications that use an external image service, without
libvips, NIFs, or image processing dependencies.
image_pipe
Added
First release of ImagePipe, with image processing powered by
elixir-image/image,
Vix, and libvips.
- Serve transformed images on demand from Phoenix or any Plug application
throughImagePipe.Plug. Reuse the same processing plans and configuration
for uploads, files, and background jobs through the Elixir API. - Read originals from local files, HTTP(S), S3-compatible storage, or custom
source adapters. - Compose resizing, cropping, orientation, effects, and watermarks in explicit
processing groups, with optional face and object detection, or your own
detector. - Define reusable presets and request defaults in your configuration,
statically or through a host preset lookup backed by a database or cache. - Encode JPEG, PNG, WebP, and AVIF, with format negotiation, quality controls,
byte budgets, and color-profile policies. Automatic quality gives each image
the lowest quality that still looks as good as a target you choose. Generate
BlurHash, LQIP CSS, and image-info JSON. - Configure input and output caches, conditional HTTP responses, streamed
delivery, request safety limits, and processing concurrency. Observe requests
through telemetry, logging, and tracing.
image_pipe_server 0.1.0
Added
First release of the standalone ImagePipe server.
- Run a dedicated HTTP image service from the published Docker images at
ghcr.io/hlindset/image_pipe_server, or build it as a Mix release. Configure
it through TOML and environment variables, without writing an Elixir
application. - Configure file, HTTP(S), and S3 sources, filesystem caches, signing keys,
presets, and processing limits. Load secrets from mounted files. - Serve images under a configurable mount path, with health checks, request
IDs, optional bearer authentication, logging, and OpenTelemetry export. - Choose a base image or a vision image with bundled face and object detection
models.
Built with image_pipe and image_pipe_url 0.1.0.