Verified against main at 61f42e6. This is the "strip EXIF and other metadata from processed images (privacy)" P1 item in TODO.md; confirmed genuinely missing — grep for StripMetadata across the repo returns nothing, and encode (internal/imageprocessor/imageprocessor.go:334-380) never sets a strip option.
libvips preserves metadata through a transform by default, so pixa currently re-publishes whatever the upstream image carried: GPS coordinates, camera serial numbers, capture timestamps, software fingerprints, and in some cases embedded thumbnails that can differ from the visible image.
This matters more for a proxy than for an ordinary encoder. An operator putting pixa in front of user-uploaded content reasonably assumes a resize strips incidental metadata, and nothing in the docs says otherwise. The embedded-thumbnail case is the sharp edge: a cropped image can leak the uncropped original.
Definition of done
Metadata is stripped on every processed output path in encode.
ICC colour profiles are handled deliberately, not dropped as collateral. Stripping the profile from a wide-gamut image without converting to sRGB first produces visibly wrong colour. Either convert to sRGB and strip, or retain the profile while stripping everything else. State the choice and reasoning in the PR.
Orientation is applied before stripping, so removing the EXIF orientation tag does not leave the image rotated.
format=orig passthrough (no transform) is decided explicitly: it is arguably a byte-for-byte proxy of the source, in which case it keeps metadata — but that must be a documented decision, not an accident. Document whichever way it goes.
Whether stripping is configurable is a judgment call; default to always-strip and only add a key if there is a real reason.
Failing tests first: a fixture with known EXIF (including GPS) produces output with no EXIF; an image with EXIF orientation renders in the correct orientation after stripping; a wide-gamut fixture does not shift colour.
README documents the behavior. make check green.
Note
A test fixture with real EXIF is needed. Keep it small — repo policy caps total repository size, and test media is one of the few things allowed to be committed, so choose a minimal image rather than a camera original.
Verified against `main` at `61f42e6`. This is the "strip EXIF and other metadata from processed images (privacy)" P1 item in `TODO.md`; confirmed genuinely missing — `grep` for `StripMetadata` across the repo returns nothing, and `encode` (`internal/imageprocessor/imageprocessor.go:334-380`) never sets a strip option.
libvips preserves metadata through a transform by default, so pixa currently re-publishes whatever the upstream image carried: GPS coordinates, camera serial numbers, capture timestamps, software fingerprints, and in some cases embedded thumbnails that can differ from the visible image.
This matters more for a proxy than for an ordinary encoder. An operator putting pixa in front of user-uploaded content reasonably assumes a resize strips incidental metadata, and nothing in the docs says otherwise. The embedded-thumbnail case is the sharp edge: a cropped image can leak the uncropped original.
## Definition of done
1. Metadata is stripped on every processed output path in `encode`.
2. **ICC colour profiles are handled deliberately**, not dropped as collateral. Stripping the profile from a wide-gamut image without converting to sRGB first produces visibly wrong colour. Either convert to sRGB and strip, or retain the profile while stripping everything else. State the choice and reasoning in the PR.
3. Orientation is applied before stripping, so removing the EXIF orientation tag does not leave the image rotated.
4. `format=orig` passthrough (no transform) is decided explicitly: it is arguably a byte-for-byte proxy of the source, in which case it keeps metadata — but that must be a documented decision, not an accident. Document whichever way it goes.
5. Whether stripping is configurable is a judgment call; default to always-strip and only add a key if there is a real reason.
6. Failing tests first: a fixture with known EXIF (including GPS) produces output with no EXIF; an image with EXIF orientation renders in the correct orientation after stripping; a wide-gamut fixture does not shift colour.
7. README documents the behavior. `make check` green.
## Note
A test fixture with real EXIF is needed. Keep it small — repo policy caps total repository size, and test media is one of the few things allowed to be committed, so choose a minimal image rather than a camera original.
clawbot
added this to the 1.0.0 milestone 2026-08-09 03:49:19 +02:00
Plan, checked against current next (encode is now at internal/imageprocessor/imageprocessor.go:362-413; still no strip option anywhere):
encode sets govips' StripMetadata on every export, so no output carries EXIF, XMP, IPTC or an ICC profile.
Right after decoding, Process turns the image upright with govips' AutoRotate, before target sizes are worked out and before export drops the orientation tag.
ICC (item 2): convert to sRGB, then strip. An image with an embedded profile is converted with govips' TransformICCProfile to sRGB before encoding. Reason: browsers show an image with no profile as sRGB, so colours stay right in every client without passing on the source's profile, which can name the device or software that made it; the cost is that colours outside sRGB are clipped.
format=orig (item 4): stripped like every other output. Reason: pixa never serves the source bytes; format=orig already decodes and re-encodes in the source's format, so there is no byte-for-byte passthrough to keep, and exempting it would only leak.
No config key: always strip.
Failing tests first, as their own commit, using three fixtures of about 1 KB each in internal/imageprocessor/testdata/: GPS EXIF, EXIF orientation 6, and a Display P3 profile. README.md documents all of this.
Model: opus-5-5
Plan, checked against current `next` (`encode` is now at `internal/imageprocessor/imageprocessor.go:362-413`; still no strip option anywhere):
- `encode` sets govips' `StripMetadata` on every export, so no output carries EXIF, XMP, IPTC or an ICC profile.
- Right after decoding, `Process` turns the image upright with govips' `AutoRotate`, before target sizes are worked out and before export drops the orientation tag.
- **ICC (item 2): convert to sRGB, then strip.** An image with an embedded profile is converted with govips' `TransformICCProfile` to sRGB before encoding. Reason: browsers show an image with no profile as sRGB, so colours stay right in every client without passing on the source's profile, which can name the device or software that made it; the cost is that colours outside sRGB are clipped.
- **`format=orig` (item 4): stripped like every other output.** Reason: pixa never serves the source bytes; `format=orig` already decodes and re-encodes in the source's format, so there is no byte-for-byte passthrough to keep, and exempting it would only leak.
- No config key: always strip.
- Failing tests first, as their own commit, using three fixtures of about 1 KB each in `internal/imageprocessor/testdata/`: GPS EXIF, EXIF orientation 6, and a Display P3 profile. `README.md` documents all of this.
Model: opus-5-5
Built as planned above in #145. One thing the plan did not name: turning the image upright before working out sizes also fixes a rotated photo asked for by width alone, whose height used to come from the sideways image.
Model: opus-5-5
Built as planned above in https://git.eeqj.de/sneak/pixa/pulls/145. One thing the plan did not name: turning the image upright before working out sizes also fixes a rotated photo asked for by width alone, whose height used to come from the sideways image.
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 "strip EXIF and other metadata from processed images (privacy)" P1 item inTODO.md; confirmed genuinely missing —grepforStripMetadataacross the repo returns nothing, andencode(internal/imageprocessor/imageprocessor.go:334-380) never sets a strip option.libvips preserves metadata through a transform by default, so pixa currently re-publishes whatever the upstream image carried: GPS coordinates, camera serial numbers, capture timestamps, software fingerprints, and in some cases embedded thumbnails that can differ from the visible image.
This matters more for a proxy than for an ordinary encoder. An operator putting pixa in front of user-uploaded content reasonably assumes a resize strips incidental metadata, and nothing in the docs says otherwise. The embedded-thumbnail case is the sharp edge: a cropped image can leak the uncropped original.
Definition of done
encode.format=origpassthrough (no transform) is decided explicitly: it is arguably a byte-for-byte proxy of the source, in which case it keeps metadata — but that must be a documented decision, not an accident. Document whichever way it goes.make checkgreen.Note
A test fixture with real EXIF is needed. Keep it small — repo policy caps total repository size, and test media is one of the few things allowed to be committed, so choose a minimal image rather than a camera original.
Plan, checked against current
next(encodeis now atinternal/imageprocessor/imageprocessor.go:362-413; still no strip option anywhere):encodesets govips'StripMetadataon every export, so no output carries EXIF, XMP, IPTC or an ICC profile.Processturns the image upright with govips'AutoRotate, before target sizes are worked out and before export drops the orientation tag.TransformICCProfileto sRGB before encoding. Reason: browsers show an image with no profile as sRGB, so colours stay right in every client without passing on the source's profile, which can name the device or software that made it; the cost is that colours outside sRGB are clipped.format=orig(item 4): stripped like every other output. Reason: pixa never serves the source bytes;format=origalready decodes and re-encodes in the source's format, so there is no byte-for-byte passthrough to keep, and exempting it would only leak.internal/imageprocessor/testdata/: GPS EXIF, EXIF orientation 6, and a Display P3 profile.README.mddocuments all of this.Model: opus-5-5
Built as planned above in #145. One thing the plan did not name: turning the image upright before working out sizes also fixes a rotated photo asked for by width alone, whose height used to come from the sideways image.
Model: opus-5-5