← Christian Bellofatto

Bender Adventure

Astro · Cloudflare Workers · R2 · D1 · live site

A place for trip photos to live that isn't a dying group chat. I shoot a lot of film-style digital on trips with friends, and every good set of photos used to meet the same fate: compressed into a thread, scrolled past, and gone in a month. Bender Adventure gives each trip a permanent gallery — invite-only, full resolution, and downloadable, so the people in the photos actually end up with them.

The constraint that shaped the whole build was cost. Photo hosting is a great way to accumulate a bill: hundreds of large files per trip, served to a handful of people, forever. Putting the originals in Cloudflare R2 means zero egress fees, so serving a 40-megapixel file to a friend who wants a print costs nothing. That single decision is why the site can stay up indefinitely without becoming a subscription I resent.

The field log

Bender Adventure home page, headed 'The trips. The crew. The evidence.', with three trip cards below: Chef Juli Freedom Feast, Ragnar Northwest Passage, and Hood River Memorial Day
Each trip gets a card with its date and photo count. Galleries are private; the index is the only public page.

Access is per event rather than per site. Each gallery carries its own password, hashed with PBKDF2 and checked in a Worker before any image key is handed out, which means I can send one trip to one group of friends without giving them the rest of the archive. The schema already has a second access mode for real accounts, for when passwords stop being enough.

Viewer

The full-screen photo viewer showing image 19 of 79 — a red barn marked Antiques and Vintage — with a camera settings plate reading M11-P, 50mm, 1/360, f4, ISO 64, and a download control
Full-screen viewer with the frame's own settings along the bottom, and a download for the original.

The caption plate reads camera, lens, shutter, aperture, and ISO straight off the file's EXIF, pulled during ingest and stored alongside the photo. It's a small thing that changes how the galleries feel — less like a shared folder and more like a contact sheet. The download button hands over the untouched original, which is the entire point: a friend who wants to print a shot at 20 inches can, without asking me to dig up the file.

Every photo is stored in three renditions — a 500px thumbnail, a 2048px preview, and the original — generated locally at ingest time rather than resized on the fly. A CLI walks a directory of JPEGs, hashes each file to derive a stable ID, reads its EXIF, builds the three sizes, and uploads them to R2. Serving then costs nothing but a key lookup.

How it's built

Astro running in a Cloudflare Worker, with D1 holding event and photo metadata and R2 holding the bytes. Images never get a public URL: a route resolves slug / size / id against the database, checks the caller's session for that event, and streams from R2 only if it passes. Photo IDs are the first twelve hex characters of the file's SHA-256, so re-running an ingest is idempotent — the same photo can't land twice, and a re-upload after a caption fix overwrites rather than duplicates.