Skip to content

A converted file can stop saying who made it - #26

Merged
thousandflowers merged 5 commits into
mainfrom
feat/privacy
Sep 5, 2026
Merged

thousandflowers merged 5 commits into
mainfrom
feat/privacy

Conversation

@thousandflowers

Copy link
Copy Markdown
Owner

Two layers, separable on purpose.

Layer 1 — metadata, deterministic and ship-ready. Three levels (keep all / remove the location / remove identifying metadata) as a step in the chain like any other, so it comes from Settings, or a preset, or a batch, or from the file's own name, in that order of specificity — and history records that it happened.

What is never removed, at any level, is the colour profile and the orientation. They are not information about a person: without them the photograph arrives on its side, in the wrong colours. The tests assert both survive every strip.

What counts as identifying is a list, not a chain of ifs about formats, and a tag on no list is carried across — a conversion that quietly loses something is worse than one that keeps too much. Maker notes are the exception that proves it: they are named after the camera that wrote them, so the shape of the key is the rule rather than a list of cameras nobody can finish. PDFs lose author, creator and producer and keep their title; exports lose location, and at the thorough level go through AVFoundation's own sharing filter.

Reading the filename had to change: only the last piece was read, so holiday_10MB_privacy.jpg lost the size. Tokens are now read from the end and stop at the first piece that is not one, which keeps 10MB_notes.jpg meaning what it always meant.

Layer 2 — assisted redaction, behind its own types. Vision reads faces and text, Natural Language reads that text for names, places and organisations, and all of it arrives as suggestions with every box unticked. The type that burns a redaction into a file takes regions, not a session, so there is no argument it can be handed that means "whatever was found". It misses things, the sheet says so where it cannot be missed, and nothing in the app calls this anonymisation.

Also fixed: choosing a preset now clears every field the previous one set — the metadata level and the size ceiling were left behind, and touching any other field let the preset go and applied the stale answer for real.

Test plan

  • swift test — green
  • GPS gone, ICC + orientation kept, asserted on a real conversion
  • PDF author cleared, pages and title intact
  • _10MB_privacy applies both; _privacy classifies as explicit, so no popup
  • Precedence: Settings < preset < name
  • Nothing redacted without confirmation; only confirmed regions burned in; the original never touched
  • Real binary: _privacy stripped GPS and camera make, kept orientation 6 and sRGB; without the token everything was carried across

🤖 Generated with Claude Code

https://claude.ai/code/session_018H9CZxWumc3ryumgEHd578

thousandflowers and others added 5 commits September 5, 2026 22:31
Three levels - keep everything, remove the location, remove what
identifies a person, a device or a moment - as a step in the chain like
any other, so it comes from Settings, or a preset, or a batch, or from
the file's own name, in that order of specificity, and history records
that it happened.

What is never removed, at any level, is the colour profile and the
orientation. They are not information about a person: without them the
photograph arrives on its side, in the wrong colours. The test asserts
both survive every strip.

What counts as identifying is a list, not a chain of ifs about formats,
and a tag on no list is carried across - a conversion that quietly loses
something is worse than one that keeps too much. Maker notes are the
exception that proves it: they are named after the camera that wrote
them, so the shape of the key is the rule rather than a list of cameras
nobody can finish.

Reading the filename had to change to do this: only the last piece was
read, so `holiday_10MB_privacy.jpg` lost the size. Tokens are now read
from the end and stop at the first piece that is not one, which keeps
`10MB_notes.jpg` meaning what it always meant.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018H9CZxWumc3ryumgEHd578
Vision reads faces and text, Natural Language reads that text for names,
places and organisations, and all of it arrives as suggestions with
every box unticked. A detector's opinion is not consent: the type that
burns a redaction into a file takes regions, not a session, so there is
no argument it can be handed that means "whatever was found".

It misses things - a face turned away, handwriting, a name in a
reflection - and the sheet says so where it cannot be missed. Nothing in
the app calls this anonymisation.

The copy is written to a place the user chose, through the same scratch
file and single move as every other write here. The original is never
the thing that gets covered.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018H9CZxWumc3ryumgEHd578
Choosing a preset filled the fields in from it and cleared most of them
first - but not the size ceiling, and not the metadata level I had just
added. Pick a preset that strips metadata, then pick one that says
nothing about it, and the chip still read "remove identifying metadata".
That is worse than a wrong-looking chip: touching any other field lets
the preset go, and from that moment the stale answer is what actually
runs.

The mapping from a preset to the sheet's fields moves onto ConvertChoice
so it can be tested at all, which is how the same bug will be caught
next time a field is added.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018H9CZxWumc3ryumgEHd578
@thousandflowers
thousandflowers merged commit 6ef174a into main Sep 5, 2026
1 check failed
@thousandflowers
thousandflowers deleted the feat/privacy branch September 5, 2026 21:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant