BREAKING CHANGE to the URL signing scheme: quality (q) and fit are now covered by the HMAC signature.
Previously q and fit were read after signature validation but folded into the variant cache key, so one signed URL replayed across q=1..100 and five fit modes produced up to 500 unauthorized cache entries and libvips transcodes (#60). The signed string now appends the effective quality and fit:
The handler already defaults an omitted q to 85 and fit to cover before verification, so those effective values are what gets signed; a URL signed for one quality/fit no longer verifies when replayed with another.
External signers must change: append :<quality>:<fit> to the signed string, using 85/cover for the defaults when the URL omits q/fit. README signature spec updated.
Owner decision — make check is RED: the new signed bytes mean the three pinned constants in internal/signature/golden_test.go can no longer match. Editing an existing test needs approval, so I left it unedited; new known-answer vectors live in golden_qualityfit_test.go (passing). Cleanest fix: delete golden_test.go (byte-pinning superseded by the new file, path-pinning by signature_test.go), or update its three constants.
Disclosures:
golden_test.go left failing pending your decision; make check red only on it.
Authoritative Docker gate couldn't complete on the shared host (builds reaped twice under memory pressure while queued on the gate lock). Cited verification: host run of internal/signature — fix and new vectors pass, only golden_test.go fails. CGO packages not run locally; the change there passes existing values through symmetric sign/verify.
model: claude-opus-4-8
**BREAKING CHANGE to the URL signing scheme:** quality (`q`) and fit are now covered by the HMAC signature.
Previously `q` and `fit` were read after signature validation but folded into the variant cache key, so one signed URL replayed across `q=1..100` and five fit modes produced up to 500 unauthorized cache entries and libvips transcodes (https://git.eeqj.de/sneak/pixa/issues/60). The signed string now appends the effective quality and fit:
`host:path:query:width:height:format:expiration:quality:fit`
The handler already defaults an omitted `q` to `85` and `fit` to `cover` before verification, so those effective values are what gets signed; a URL signed for one quality/fit no longer verifies when replayed with another.
**External signers must change:** append `:<quality>:<fit>` to the signed string, using `85`/`cover` for the defaults when the URL omits `q`/`fit`. README signature spec updated.
**Owner decision — `make check` is RED:** the new signed bytes mean the three pinned constants in `internal/signature/golden_test.go` can no longer match. Editing an existing test needs approval, so I left it unedited; new known-answer vectors live in `golden_qualityfit_test.go` (passing). Cleanest fix: delete `golden_test.go` (byte-pinning superseded by the new file, path-pinning by `signature_test.go`), or update its three constants.
Disclosures:
- `golden_test.go` left failing pending your decision; `make check` red only on it.
- Authoritative Docker gate couldn't complete on the shared host (builds reaped twice under memory pressure while queued on the gate lock). Cited verification: host run of `internal/signature` — fix and new vectors pass, only `golden_test.go` fails. CGO packages not run locally; the change there passes existing values through symmetric sign/verify.
model: claude-opus-4-8
Blocking finding: make check is red on this head. TestSigner_GoldenVectors in internal/signature/golden_test.go fails — its three pinned constants still encode the old signed format (…:expiration), which the new …:expiration:quality:fit format necessarily breaks. A branch that fails make check cannot be squash-merged into next, which must stay green and mergeable to main. This is issue #60 done-criteria 5 (make check green) unmet.
Acceptable: golden_test.go is brought green — either its three constants are updated to the new format, or the file is removed as superseded — but both edit an existing test, which needs the owner's approval per the iron rule. That decision was correctly surfaced (issue-60 plan comment and PR body) and is the sole thing standing between this and mergeable; until the owner rules and the tree is green, it goes back.
Minor: PR body is 252 words; trim toward the ~250 ceiling when reworking, since it becomes the squash commit message.
Verdict: FAIL
model: claude-opus-4-8
Blocking finding: `make check` is red on this head. `TestSigner_GoldenVectors` in `internal/signature/golden_test.go` fails — its three pinned constants still encode the old signed format (`…:expiration`), which the new `…:expiration:quality:fit` format necessarily breaks. A branch that fails `make check` cannot be squash-merged into `next`, which must stay green and mergeable to `main`. This is issue https://git.eeqj.de/sneak/pixa/issues/60 done-criteria 5 (`make check` green) unmet.
Acceptable: `golden_test.go` is brought green — either its three constants are updated to the new format, or the file is removed as superseded — but both edit an existing test, which needs the owner's approval per the iron rule. That decision was correctly surfaced (issue-60 plan comment and PR body) and is the sole thing standing between this and mergeable; until the owner rules and the tree is green, it goes back.
Minor: PR body is 252 words; trim toward the ~250 ceiling when reworking, since it becomes the squash commit message.
Verdict: FAIL
model: claude-opus-4-8
Waiting on the owner ruling on #60 (approve the golden-vector update, or switch to option (b)). Rework follows the ruling.
model: claude-fable-5
Waiting on the owner ruling on https://git.eeqj.de/sneak/pixa/issues/60 (approve the golden-vector update, or switch to option (b)). Rework follows the ruling.
model: claude-fable-5
Add Quality and FitMode fields to signature.Request and a failing test
that signs a request for quality 85 / fit cover and replays it with a
different quality or fit mode. The replay currently verifies, proving
the amplification vector: one signed URL authorizes any quality and fit,
yielding unauthorized cache entries and transcodes. The fields are inert
here; the next commit makes the signature cover them.
model: claude-opus-4-8
Quality (q) and fit were read after signature validation and folded
into the variant cache key, so one signed URL could be replayed across
100 quality values and 5 fit modes, yielding up to 500 unauthorized
cache entries and libvips transcodes.
The signed data now appends the effective quality and fit:
host:path:query:width:height:format:expiration:quality:fit
The handler already defaults an omitted q to 85 and fit to cover before
verification, so those effective values are what gets signed; a URL
signed for one quality or fit no longer verifies when replayed with
another. This is a breaking change to the URL signing scheme: external
signers must append :<quality>:<fit> to the signed string.
New known-answer vectors are added in golden_qualityfit_test.go; the
README signature specification is updated. The pre-existing
golden_test.go pins the old signed bytes and can no longer stay green;
it is left unedited per instruction pending an owner decision.
model: claude-opus-4-8
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.
BREAKING CHANGE to the URL signing scheme: quality (
q) and fit are now covered by the HMAC signature.Previously
qandfitwere read after signature validation but folded into the variant cache key, so one signed URL replayed acrossq=1..100and five fit modes produced up to 500 unauthorized cache entries and libvips transcodes (#60). The signed string now appends the effective quality and fit:host:path:query:width:height:format:expiration:quality:fitThe handler already defaults an omitted
qto85andfittocoverbefore verification, so those effective values are what gets signed; a URL signed for one quality/fit no longer verifies when replayed with another.External signers must change: append
:<quality>:<fit>to the signed string, using85/coverfor the defaults when the URL omitsq/fit. README signature spec updated.Owner decision —
make checkis RED: the new signed bytes mean the three pinned constants ininternal/signature/golden_test.gocan no longer match. Editing an existing test needs approval, so I left it unedited; new known-answer vectors live ingolden_qualityfit_test.go(passing). Cleanest fix: deletegolden_test.go(byte-pinning superseded by the new file, path-pinning bysignature_test.go), or update its three constants.Disclosures:
golden_test.goleft failing pending your decision;make checkred only on it.internal/signature— fix and new vectors pass, onlygolden_test.gofails. CGO packages not run locally; the change there passes existing values through symmetric sign/verify.model: claude-opus-4-8
Blocking finding:
make checkis red on this head.TestSigner_GoldenVectorsininternal/signature/golden_test.gofails — its three pinned constants still encode the old signed format (…:expiration), which the new…:expiration:quality:fitformat necessarily breaks. A branch that failsmake checkcannot be squash-merged intonext, which must stay green and mergeable tomain. This is issue #60 done-criteria 5 (make checkgreen) unmet.Acceptable:
golden_test.gois brought green — either its three constants are updated to the new format, or the file is removed as superseded — but both edit an existing test, which needs the owner's approval per the iron rule. That decision was correctly surfaced (issue-60 plan comment and PR body) and is the sole thing standing between this and mergeable; until the owner rules and the tree is green, it goes back.Minor: PR body is 252 words; trim toward the ~250 ceiling when reworking, since it becomes the squash commit message.
Verdict: FAIL
model: claude-opus-4-8
Waiting on the owner ruling on #60 (approve the golden-vector update, or switch to option (b)). Rework follows the ruling.
model: claude-fable-5
af47215237to460c11a7bfView command line instructions
Checkout
From your project repository, check out a new branch and test the changes.