v2 Session: Lessons Learned & Technical Insights

Date: 2026-08-31 • Context: Implementing metadata embedding via APP15

Key Insight: Graceful Degradation

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.

Technical Patterns

1. Metadata Extraction Strategy

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

2. Backward Compatibility by Default

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

3. Metadata Integrity via Key-Check

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)

What Went Well

✓ 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

What Would I Do Differently

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)

For v2.1+

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

Conclusion

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.

tags v2, lessons, metadata, app15