Skip to main content
User uploads go live faster when nobody has to look at each one, but you still need two things: no unsafe image published, and no stranger’s face or plate published by accident. This pipeline asks a safety model first, then asks a vision model for boxes around anything that identifies a person, blurs them with ffmpeg, and writes one line per decision to an append-only log with both hashes, so you can show later what was published and why. It costs about a twentieth of a cent an image.

Build it in your project

Paste this line into your agent (Claude Code, Codex, Cursor, OpenCode, Cline). It sets up your key, then builds the pipeline in your project:

The code

One file per language. Both read RUNCRATE_API_KEY from the environment; nothing secret is in the code.

What the proof run cost

The Python file on three generated test uploads: a man beside a parked car, a living room with a laptop and a passport, and a pair of shoes. Measured on 2026-10-23 against api.runcrate.ai; costs are what the account was billed, read from each response’s X-LLM-Cost header or usage.cost. The TypeScript file on the same three: the same decisions and $0.001160, in 34 s. No test image was unsafe, so the reject branch and its webhook did not fire in the proof.

Scale it

  • Run it where uploads land: call it from your upload handler, or have a schedule sweep a bucket.
  • Cost per image is fixed: two small vision calls. A million images is about $390 at the proof’s rate.
  • Keep the audit log on a volume when it runs on a job, so it outlives the machine.

Gotchas found in the proof run

  • A short prompt (“find regions that identify a person”) missed the main face in the street photo. Listing every kind, asking to count the people first, and naming small, partial and on-screen faces fixed it on both Qwen3-VL models.
  • meta-llama/Llama-Guard-4-12B answered 429 “busy” on all three images on the proof day; Nemotron Content Safety 3.5 answered at once. The script tries one, then the other.
  • Qwen3-VL gives boxes on a 0 to 1000 grid of the image, whatever its size; scale them to pixels before you crop. The script also pads each box by 10% so the blur covers the edge of a face.
  • The 30B Qwen3-VL found the passport that the 235B one missed on the same prompt. Bigger was not better here: test on your own images.