Go · JPEG metadata · stdlib-only

Edit the metadata. Leave every other byte alone.

Byte-level parsing and minimal-diff editing of a JPEG's segments, EXIF/TIFF block, XMP packet and IPTC-IIM datasets. Bytes in, bytes out — you own the policy.

go get codeberg.org/elkarrde/exifscalpel@latest

Go 1.22+Zero runtime depsMPL-2.0

APP1 · Exif · IFD0 Software (0x0131)
0000 FF E1 00 37 45 78 69 66 00 00 4D 4D 00 2A 00 00 ...7Exif..MM.*.....7Exif..MM.*..
0010 00 08 00 01 01 31 00 02 00 00 00 15 00 00 00 1A .....1...............1..........
0020 00 00 00 00 4100 6400 6F00 6200 6500 2000 5000 6800 6F00 7400 6F00 7300 ....Adobe Photos................
0030 6800 6F00 7000 2000 3200 3000 3200 3400 00 FF DB 00 43 00 08 06 hop 2024....C...............C...
020 bytes changed 0 offsets moved Segment length 0x0037 unchanged

Why it exists

Go's metadata libraries read. This one writes — surgically.

Minimal diff, by default

OverwriteValueInPlace and xmp.Clean preserve segment length, so every JPEG and TIFF offset stays put. Rebuild with Build only when a field count has to change.

Zero runtime dependencies

Standard library only. No go.sum, nothing transitive — and it compiles to GOOS=js GOARCH=wasm without friction.

Primitives, not policy

No flags, printing or file walking. What to strip and what to keep lives in your code; the library does the byte surgery.

Examples

A few lines per job.

sanitize.goGo
func sanitize(dst io.Writer, r io.Reader) error {	segs, tail, err := jpeg.Parse(r)	if err != nil {		return err // not a JPEG	}	kept := segs[:0]	for _, s := range segs {		if jpeg.IsEXIF(s) || jpeg.IsXMP(s) {			continue // policy is yours		}		kept = append(kept, s)	}	return jpeg.Write(dst, kept, tail)}

Where it fits

Built for these jobs.

  • Upload sanitizer

    Scrub on ingest inside an HTTP handler. Reader in, writer out, no allocation-heavy model.

    Reachable today
  • Location stripping

    Remove GPS from EXIF, location fields from XMP and IPTC — nothing else touched.

    Reachable today
  • Metadata diffing

    Parse two files and compare IFD0, Exif and GPS entries by tag and value bytes.

    Reachable today
  • In-browser strip (WASM)

    Strip EXIF before a photo leaves the device. Core is ready; needs a syscall/js shim.

    Needs a thin layer
  • Provenance-safe edits

    Locate or preserve C2PA APP11 segments; length-preserving edits keep offsets stable.

    Needs a thin layer