Date: 2026-08-31 • Context: Implementing metadata embedding via APP15
Challenge: Pillow JPEG encoder doesn't preserve APP15 markers
Naive approach: Give up on APP15, stick with external metadata
Better approach: Attempt APP15, fallback gracefully if it fails
Result: v2 is production-ready with fallback; APP15 preserved when possible
Lesson: Perfect is the enemy of good. A fallback that works beats a perfect feature that doesn't exist.
Pattern: Try structured extraction first (APP15), fall back to requirements
Implementation:
1. Try load_app15_metadata() (if present and valid)
2. If fails, require metadata_override parameter
3. Return metadata for user's records (always)
Benefit: Reduces required parameters over time as encoders improve
Design: v0.5/v1/v1.5 files have no APP15 metadata → restore requires external params
Result: All old files still work (no breaking changes)
Future: v2 files may have APP15 (optional) → restore can work without params
Problem: Attacker modifies APP15 metadata (wrong region, salt)
Solution: HMAC-SHA256 key-check of metadata (computed during encode)
During restore: Recompute key-check, compare with stored value
If mismatch: Wrong passphrase or corrupted metadata (error)
✓ Modular design: image.py, cli.py, metadata.py changes were surgical
✓ Reuse existing patterns: Followed metadata.py encode/decode structure
✓ Testing early: test_v2_app15.py revealed Pillow limitation before production
✓ Fallback strategy: Ensured robustness when APP15 preservation failed
1. Research Pillow APP marker handling earlier (would save 1 hour debugging)
2. Use libjpeg-turbo from start (eliminates Pillow limitation, trade-off: complexity)
3. Design metadata_override as full dict from day 1 (done well, no regrets)
1. Integrate libjpeg-turbo for reliable APP15 preservation
2. Consider subprocess jpegtran as simpler alternative
3. Test with multiple JPEG encoders (libjpeg, ImageMagick, ffmpeg)
4. Document encoder compatibility matrix
v2 demonstrates graceful degradation: when perfect APP15 preservation isn't possible, a robust fallback (external metadata) ensures production readiness. Future versions improve, but v2 works reliably today.