Our family photo library has 247,658 photos in it. Twenty years of kids, sunsets, dogs, and blurry thumbs. I wanted Bubbles — the assistant the family already texts — to be able to answer “find the pictures from the boat day last summer” without me scrolling for ten minutes.

Which means giving an AI agent access to the single most irreplaceable dataset we own. So the design work wasn’t about what to let it do. It was about what to make impossible.

Seven verbs

Here’s what shipped. A small signed native app is the only thing on the machine with photo-library access. It exposes exactly seven operations over a locked-down local socket: check status, search, build a contact sheet, describe a photo, export a copy, create an album, add to an album. That’s the entire vocabulary. Seven verbs.

The verbs that don’t exist

Now the verbs that don’t exist: delete. Edit. Hide. Favorite. Rewrite metadata. Remove from album. Not “disallowed by policy” — the operations were never built. There is no code path from the AI to a destructive action, which means no bug, no prompt injection, and no confidently wrong model can find one. The most dangerous thing Bubbles can do to our photo library is make an album I didn’t ask for, and even album changes require a flag asserting I explicitly asked. Every mutation is written to an audit log. Adding the same photo to an album twice politely reports “already there” instead of duplicating it.

The photos stay home

The second decision I care about: the captioning — the part where a vision model looks at each photo and writes down what it sees — runs locally, on Bubbles, on an open-weights model. Family photos don’t get shipped to any cloud provider to be understood. The only thing that leaves the house is the derived text of the captions, which goes out for embedding so search works. The photos stay home. The photo indexer doesn’t even have a cloud API key to leak.

A deliberately slow backfill

The backfill is deliberately unambitious. Indexing runs every 15 minutes, 25 captions a batch — about 100 photos an hour — and the initial pass is capped at the newest 5,000 photos, not all 247,658. Doing the whole library at that rate would take about 103 days, and I haven’t turned that on. The newest five thousand cover almost every real question we ask; the 2009 archive can wait until the system has earned it.

Did it work? The end-to-end canary passed: one photo captioned, indexed, and findable through the assistant. Then a hundred in a row with zero failures. The test-album mutation behaved, the duplicate-add check behaved, and the one overly broad system permission that snuck in during development got revoked after verification, with a snapshot kept in case I ever need to prove what changed.

The pattern generalizes, I think. My first instinct with AI agents was to ask what capabilities to add. With anything I actually care about, I’m trying to ask the other question first: which capabilities should be permanently withheld? Seven verbs has been plenty here.