From e4f7eada71dbb32b386b068c12380bb6b4de5e75 Mon Sep 17 00:00:00 2001 From: Will Norris Date: Wed, 7 Jan 2026 09:44:59 -0800 Subject: [PATCH] docs: reformat plugin-design.md for semantic line breaks I'm hoping to pick this idea back up soon. --- docs/plugin-design.md | 176 +++++++++++++++++------------------------- 1 file changed, 72 insertions(+), 104 deletions(-) diff --git a/docs/plugin-design.md b/docs/plugin-design.md index 8369332..d416144 100644 --- a/docs/plugin-design.md +++ b/docs/plugin-design.md @@ -4,38 +4,28 @@ ## Objective -Rearchitect imageproxy to use a plugin-based system for most features like -transformations, security, and caching. This should reduce build times and -binary sizes in the common case, and provide a mechanism for users to easily -add custom features that would not be added to core for various reasons. +Re-architect imageproxy to use a plugin-based system for most features like transformations, security, and caching. +This should reduce build times and binary sizes in the common case, +and provide a mechanism for users to easily add custom features that would not be added to core for various reasons. ## Background -I created imageproxy to [scratch a personal itch](https://wjn.me/b/J_), I -needed a simple way to dynamically resize images for my personal website. I -published it as an open source projects because that's what I do, and I'm happy -to see others finding it useful for their needs as well. +I created imageproxy to [scratch a personal itch](https://wjn.me/b/J_), I needed a simple way to dynamically resize images for my personal website. +I published it as an open source projects because that's what I do, and I'm happy to see others finding it useful for their needs as well. -But inevitably, with more users came requests for additional features because -people have different use cases and requirements. Some of these requests were -relatively minor, and I was happy to add them. But one of the more common -requests was to support different caching backends. Personally, I still use the -on-disk cache, but many people wanted to use redis or a cloud provider like -AWS, Azure, or GCP. For a long time I was resistant to adding support for -these, mainly out of concern for inflating build times and binary sizes. I did -eventually relent, and -[#49](https://github.com/willnorris/imageproxy/issues/49) tracked adding -support for the most common backends. +But inevitably, with more users came requests for additional features because people have different use cases and requirements. +Some of these requests were relatively minor, and I was happy to add them. +But one of the more common requests was to support different caching backends. +Personally, I still use the on-disk cache, but many people wanted to use redis or a cloud provider like AWS, Azure, or GCP. +For a long time I was resistant to adding support for these, mainly out of concern for inflating build times and binary sizes. +I did eventually relent, and [#49] tracked adding support for the most common backends. -Unfortunately my concerns proved true, and build times are _significantly_ -slower (TODO: add concrete numbers) now because of all the additional cloud -SDKs that get compiled in. I don't personally care too much about binary size, -since I'm not running in a constrained environment, but these build times are -really wearing on me. Additionally, there are a number of outstanding pull -requests for relatively obscure features that I don't really want to have to -support in the main project. And quite honestly, there are a number of obscure -features that did get merged in over the years that I kinda wish I could rip -back out. +Unfortunately my concerns proved true, and build times are _significantly_ slower (TODO: add concrete numbers) now because of all the additional cloud SDKs that get compiled in. +I don't personally care too much about binary size, since I'm not running in a constrained environment, but these build times are really wearing on me. +Additionally, there are a number of outstanding pull requests for relatively obscure features that I don't really want to have to support in the main project. +And quite honestly, there are a number of obscure features that did get merged in over the years that I kinda wish I could rip back out. + +[#49]: https://github.com/willnorris/imageproxy/issues/49 ### Plugin support in Go @@ -44,43 +34,32 @@ TODO: talk about options like - RPC () - pkg/plugin () - embedded interpreter () -- custom binaries (, - ) +- custom binaries (, ) -Spoiler: I'm planning on following the Caddy approach and using custom -binaries. +Spoiler: I'm planning on following the Caddy approach and using custom binaries. ## Design -I plan to model imageproxy after Caddy, moving all key functionality into -separate plugins that register themselves with the server, and which all -compile to a single statically-linked binary. The core project will provide a -great number of plugins to cover all of the existing functionality. I also -expect I'll be much more open to adding plugins for features I may not care as -much about personally. Of course, users can also write their own plugins and -link them in without needing to contribute them to core if they don't want to. +I plan to model imageproxy after Caddy, moving all key functionality into separate plugins that register themselves with the server, +and which all compile to a single statically-linked binary. +The core project will provide a great number of plugins to cover all of the existing functionality. +I also expect I'll be much more open to adding plugins for features I may not care as much about personally. +Of course, users can also write their own plugins and link them in without needing to contribute them to core if they don't want to. I anticipate providing two or three build configurations in core: -- **full** - include all the plugins that are part of core (except where they - may conflict) -- **minimal** - some set of minimal features that only includes basic caching - options, limited transformation options, etc -- **my personal config** - I'll also definitely have a build that I use - personally on my site. I may decide to just make that the "minimal" build - and perhaps call it something different, rather than have a third - configuration. +- **full** - include all the plugins that are part of core (except where they may conflict) +- **minimal** - some set of minimal features that only includes basic caching options, limited transformation options, etc +- **my personal config** - I'll also definitely have a build that I use personally on my site. + I may decide to just make that the "minimal" build and perhaps call it something different, rather than have a third configuration. -Custom configurations beyond what is provided by core can be done by creating a -minimal main package that imports the plugins you care about and calling some -kind of bootstrap method (similar to [what Caddy now -does](https://caddy.community/t/59)). +Custom configurations beyond what is provided by core can be done by creating a minimal main package that imports the plugins you care about +and calling some kind of bootstrap method (similar to [what Caddy now does](https://caddy.community/t/59)). ### Types of plugins -(Initially in no particular order, just capturing thoughts. Lots to do here in -thinking through the use cases and what kind of plugin API we really need to -provide.) +(Initially in no particular order, just capturing thoughts. +Lots to do here in thinking through the use cases and what kind of plugin API we really need to provide.) See also issues and PRs with [label:plugins][]. @@ -88,70 +67,61 @@ See also issues and PRs with [label:plugins][]. #### Caching backend -This is one of the most common feature requests, and is also one of the worst -offender for inflating build times and binary sizes because of the size of the -dependencies that are typically required. The minimal imageproxy build would -probably only include the in-memory and on-disk caches. Anything that talked to -an external store (redis, cloud providers, etc) would be pulled out. +This is one of the most common feature requests, and is also one of the worst offender for inflating build times +and binary sizes because of the size of the dependencies that are typically required. +The minimal imageproxy build would probably only include the in-memory and on-disk caches. +Anything that talked to an external store (redis, cloud providers, etc) would be pulled out. #### Transformation engine -Today, imageproxy only performs transformations which can be done with pure Go -libraries. There have been a number of requests (or at least questions) to use -something like [vips](https://github.com/DAddYE/vips) or -[imagemagick](https://github.com/gographics/imagick), which are both C -libraries. They provide more options, and (likely) better performance, at the -cost of complexity and loss of portability in using cgo. These would likely -replace the entire transformation engine in imageproxy, so I don't know how -they would interact with other plugins that merely extend the main engine (they -probably wouldn't be able to interact at all). +Today, imageproxy only performs transformations which can be done with pure Go libraries. +There have been a number of requests (or at least questions) to use something like [vips] or [imagemagick], which are both C libraries. +They provide more options, and (likely) better performance, at the cost of complexity and loss of portability in using cgo. +These would likely replace the entire transformation engine in imageproxy, +so I don't know how they would interact with other plugins that merely extend the main engine (they probably wouldn't be able to interact at all). + +[vips]: https://github.com/DAddYE/vips +[imagemagick]: https://github.com/gographics/imagick #### Transformation options -Today, imageproxy performs minimal transformations, mostly around resizing, -cropping, and rotation. It doesn't support any kind of filters, brightness or -contrast adjustment, etc. There are go libraries for them, they're just outside -the scope of what I originally intended imageproxy for. But I'd be happy to -have plugins that do that kind of thing. These plugins would need to be able to -hook into the option parsing engine so that they could register their URL -options. +Today, imageproxy performs minimal transformations, mostly around resizing, cropping, and rotation. +It doesn't support any kind of filters, brightness or contrast adjustment, etc. +There are go libraries for them, they're just outside the scope of what I originally intended imageproxy for. +But I'd be happy to have plugins that do that kind of thing. +These plugins would need to be able to hook into the option parsing engine so that they could register their URL options. #### Image format support -There have been a number of requests for imge format support that require cgo -libraries: +There have been a number of requests for image format support that require cgo libraries: -- **webp encoding** - needs cgo - [#114](https://github.com/willnorris/imageproxy/issues/114) -- **progressive jpegs** - probably needs cgo? - [#77](https://github.com/willnorris/imageproxy/issues/77) -- **gif to mp4** - maybe doable in pure go, but probably belongs in a plugin - [#136](https://github.com/willnorris/imageproxy/issues/136) -- **HEIF** - formate used by newer iPhones - ([HEIF](https://en.wikipedia.org/wiki/High_Efficiency_Image_File_Format)) +- **webp encoding** - needs cgo [#114](https://github.com/willnorris/imageproxy/issues/114) +- **progressive jpegs** - probably needs cgo? [#77](https://github.com/willnorris/imageproxy/issues/77) +- **gif to mp4** - maybe doable in pure go, but probably belongs in a plugin [#136](https://github.com/willnorris/imageproxy/issues/136) +- **HEIF** - formate used by newer iPhones ([HEIF](https://en.wikipedia.org/wiki/High_Efficiency_Image_File_Format)) #### Option parsing -Today, options are specified as the first component in the URL path, but -[#66](https://github.com/willnorris/imageproxy/pull/66) proposes optionally -moving that to a query parameter (for a good reason, actually). Maybe putting -that in core is okay? Maybe it belongs in a plugin, in which case we'd need to -expose an API for replacing the option parsing code entirely. +Today, options are specified as the first component in the URL path, but [#66] proposes optionally moving that to a query parameter (for a good reason, actually). +Maybe putting that in core is okay? +Maybe it belongs in a plugin, in which case we'd need to expose an API for replacing the option parsing code entirely. + +[#66]: https://github.com/willnorris/imageproxy/pull/66 #### Security options -Some people want to add a host blacklist -[#85](https://github.com/willnorris/imageproxy/pull/85), refusal to process -non-image files [#53](https://github.com/willnorris/imageproxy/issues/53) -[#119](https://github.com/willnorris/imageproxy/pull/119). I don't think there -is an issue for it, but an early fork of the project added request signing that -was compatible with nginx's [secure link -module](https://nginx.org/en/docs/http/ngx_http_secure_link_module.html). +Some people want to add a host blacklist [#85], refusal to process non-image files [#53] [#119]. +I don't think there is an issue for it, +but an early fork of the project added request signing that was compatible with nginx's [secure link module](https://nginx.org/en/docs/http/ngx_http_secure_link_module.html). + +[#85]: https://github.com/willnorris/imageproxy/pull/85 +[#53]: https://github.com/willnorris/imageproxy/issues/53 +[#119]: https://github.com/willnorris/imageproxy/pull/119 ### Registering Plugins -Plugins are loaded simply by importing their package. They should have an -`init` func that calls `imageproxy.RegisterPlugin`: +Plugins are loaded simply by importing their package. +They should have an `init` func that calls `imageproxy.RegisterPlugin`: ```go type Plugin struct { @@ -160,9 +130,8 @@ type Plugin struct { func RegisterPlugin(name string, plugin Plugin) ``` -Plugins hook into various extension points of imageproxy by implementing -appropriate interfaces. A single plugin can hook into multiple parts of -imageproxy by implementing multiple interfaces. +Plugins hook into various extension points of imageproxy by implementing appropriate interfaces. +A single plugin can hook into multiple parts of imageproxy by implementing multiple interfaces. For example, two possible interfaces for security related plugins: @@ -196,6 +165,5 @@ type ImageTransformer interface { } ``` -Plugins are additionally responsible for registering any additional command -line flags they wish to expose to the user, as well as storing any global state -that would previously have been stored on the Proxy struct. +Plugins are additionally responsible for registering any additional command line flags they wish to expose to the user, +as well as storing any global state that would previously have been stored on the Proxy struct.