Verified against main at 61f42e6. This is the "auto format selection (format=auto based on Accept header)" item in TODO.md; confirmed missing — parseFormat (internal/imgcache/urlparser.go:241-258) has no auto case, and the client's Accept header is never read anywhere.
This is the feature that makes a proxy like pixa worth deploying: serve WebP or AVIF to clients that accept them, JPEG to those that do not, from a single URL. Without it, callers must know each client's capabilities at URL-construction time, which is exactly what they were trying to avoid.
Definition of done
format=auto selects the best format the client accepts, preferring AVIF, then WebP, then falling back to the source format (or JPEG). The preference order is documented.
Accept parsing handles quality values (q=) and wildcards (image/*, */*) correctly. A client sending no Accept gets the fallback.
Vary: Accept is set on every negotiated response. Without it a shared cache will serve AVIF to a client that cannot render it — a correctness bug, not an optimization. See the related response-headers issue.
The cache key includes the resolved format, not the literal auto, so negotiated variants do not collide.
Signature interaction is decided explicitly: the signature covers format, so a URL signed for auto must validate against auto (the literal requested value) rather than the resolved one — otherwise the signature depends on the client's headers and becomes unverifiable. Add a test pinning this, and coordinate with the issue that adds q/fit to the signed payload.
Repo rule: format=auto must never silently produce a format the client explicitly rejected. If nothing acceptable can be produced, return an error rather than guessing.
Failing tests first, table-driven over Accept values: AVIF-capable, WebP-capable, neither, wildcard-only, absent, and a malformed header.
README documents auto in the format list. make check green.
Verified against `main` at `61f42e6`. This is the "auto format selection (format=auto based on Accept header)" item in `TODO.md`; confirmed missing — `parseFormat` (`internal/imgcache/urlparser.go:241-258`) has no `auto` case, and the client's `Accept` header is never read anywhere.
This is the feature that makes a proxy like pixa worth deploying: serve WebP or AVIF to clients that accept them, JPEG to those that do not, from a single URL. Without it, callers must know each client's capabilities at URL-construction time, which is exactly what they were trying to avoid.
## Definition of done
1. `format=auto` selects the best format the client accepts, preferring AVIF, then WebP, then falling back to the source format (or JPEG). The preference order is documented.
2. `Accept` parsing handles quality values (`q=`) and wildcards (`image/*`, `*/*`) correctly. A client sending no `Accept` gets the fallback.
3. **`Vary: Accept` is set on every negotiated response.** Without it a shared cache will serve AVIF to a client that cannot render it — a correctness bug, not an optimization. See the related response-headers issue.
4. The cache key includes the *resolved* format, not the literal `auto`, so negotiated variants do not collide.
5. Signature interaction is decided explicitly: the signature covers `format`, so a URL signed for `auto` must validate against `auto` (the literal requested value) rather than the resolved one — otherwise the signature depends on the client's headers and becomes unverifiable. Add a test pinning this, and coordinate with the issue that adds `q`/`fit` to the signed payload.
6. Repo rule: `format=auto` must never silently produce a format the client explicitly rejected. If nothing acceptable can be produced, return an error rather than guessing.
7. Failing tests first, table-driven over `Accept` values: AVIF-capable, WebP-capable, neither, wildcard-only, absent, and a malformed header.
8. README documents `auto` in the format list. `make check` green.
clawbot
added this to the 1.0.0 milestone 2026-08-09 03:50:49 +02:00
Plan, checked against next at 01823d2. Still missing: parseFormat (internal/imgcache/urlparser.go) has no auto, and no response depends on Accept.
auto becomes a format value wherever a format is accepted: the /v1/image/ path suffix (<size>.auto), the format in an encrypted URL's token, and the generator page's format choice.
It is resolved per request from Accept: AVIF if acceptable, else WebP, else JPEG. Quality values (q=), q=0 refusals and wildcards (image/*, */*) are honoured; no Accept header means JPEG. If none of the three is acceptable, the answer is 406 with the usual JSON error; pixa never sends a format the client refused. The order is documented in README.md.
The signature and the encrypted token cover the literal auto, not the resolved format, so a signed URL stays valid whatever the client sends; a test pins this.
The cache key uses the resolved format, so the AVIF, WebP and JPEG variants of one auto URL are cached apart and a cache hit for one never answers another.
Every response to an auto URL carries Vary: Accept (also on 304 and HEAD), next to the Vary: Origin the CORS library already sends; responses to fixed-format URLs do not.
Tests first, table-driven over Accept: AVIF-capable, WebP-only, neither, wildcard only, absent, q=0 on AVIF, and a malformed header; plus the signature and cache-key tests above and Vary on both routes.
Model: opus-5-5
Plan, checked against `next` at `01823d2`. Still missing: `parseFormat` (`internal/imgcache/urlparser.go`) has no `auto`, and no response depends on `Accept`.
- `auto` becomes a format value wherever a format is accepted: the `/v1/image/` path suffix (`<size>.auto`), the format in an encrypted URL's token, and the generator page's format choice.
- It is resolved per request from `Accept`: AVIF if acceptable, else WebP, else JPEG. Quality values (`q=`), `q=0` refusals and wildcards (`image/*`, `*/*`) are honoured; no `Accept` header means JPEG. If none of the three is acceptable, the answer is 406 with the usual JSON error; pixa never sends a format the client refused. The order is documented in `README.md`.
- The signature and the encrypted token cover the literal `auto`, not the resolved format, so a signed URL stays valid whatever the client sends; a test pins this.
- The cache key uses the resolved format, so the AVIF, WebP and JPEG variants of one `auto` URL are cached apart and a cache hit for one never answers another.
- Every response to an `auto` URL carries `Vary: Accept` (also on 304 and `HEAD`), next to the `Vary: Origin` the CORS library already sends; responses to fixed-format URLs do not.
- Tests first, table-driven over `Accept`: AVIF-capable, WebP-only, neither, wildcard only, absent, `q=0` on AVIF, and a malformed header; plus the signature and cache-key tests above and `Vary` on both routes.
Model: opus-5-5
Built in #220. One deviation from the plan above: image/* and */* allow and refuse JPEG but do not make AVIF or WebP acceptable; only naming image/avif or image/webp does, because browsers that cannot show those formats send the wildcards too.
Model: opus-5-5
Built in https://git.eeqj.de/sneak/pixa/pulls/220. One deviation from the plan above: `image/*` and `*/*` allow and refuse JPEG but do not make AVIF or WebP acceptable; only naming `image/avif` or `image/webp` does, because browsers that cannot show those formats send the wildcards too.
Model: opus-5-5
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Verified against
mainat61f42e6. This is the "auto format selection (format=auto based on Accept header)" item inTODO.md; confirmed missing —parseFormat(internal/imgcache/urlparser.go:241-258) has noautocase, and the client'sAcceptheader is never read anywhere.This is the feature that makes a proxy like pixa worth deploying: serve WebP or AVIF to clients that accept them, JPEG to those that do not, from a single URL. Without it, callers must know each client's capabilities at URL-construction time, which is exactly what they were trying to avoid.
Definition of done
format=autoselects the best format the client accepts, preferring AVIF, then WebP, then falling back to the source format (or JPEG). The preference order is documented.Acceptparsing handles quality values (q=) and wildcards (image/*,*/*) correctly. A client sending noAcceptgets the fallback.Vary: Acceptis set on every negotiated response. Without it a shared cache will serve AVIF to a client that cannot render it — a correctness bug, not an optimization. See the related response-headers issue.auto, so negotiated variants do not collide.format, so a URL signed forautomust validate againstauto(the literal requested value) rather than the resolved one — otherwise the signature depends on the client's headers and becomes unverifiable. Add a test pinning this, and coordinate with the issue that addsq/fitto the signed payload.format=automust never silently produce a format the client explicitly rejected. If nothing acceptable can be produced, return an error rather than guessing.Acceptvalues: AVIF-capable, WebP-capable, neither, wildcard-only, absent, and a malformed header.autoin the format list.make checkgreen.Plan, checked against
nextat01823d2. Still missing:parseFormat(internal/imgcache/urlparser.go) has noauto, and no response depends onAccept.autobecomes a format value wherever a format is accepted: the/v1/image/path suffix (<size>.auto), the format in an encrypted URL's token, and the generator page's format choice.Accept: AVIF if acceptable, else WebP, else JPEG. Quality values (q=),q=0refusals and wildcards (image/*,*/*) are honoured; noAcceptheader means JPEG. If none of the three is acceptable, the answer is 406 with the usual JSON error; pixa never sends a format the client refused. The order is documented inREADME.md.auto, not the resolved format, so a signed URL stays valid whatever the client sends; a test pins this.autoURL are cached apart and a cache hit for one never answers another.autoURL carriesVary: Accept(also on 304 andHEAD), next to theVary: Originthe CORS library already sends; responses to fixed-format URLs do not.Accept: AVIF-capable, WebP-only, neither, wildcard only, absent,q=0on AVIF, and a malformed header; plus the signature and cache-key tests above andVaryon both routes.Model: opus-5-5
Built in #220. One deviation from the plan above:
image/*and*/*allow and refuse JPEG but do not make AVIF or WebP acceptable; only namingimage/aviforimage/webpdoes, because browsers that cannot show those formats send the wildcards too.Model: opus-5-5