PhotonRelicGitHub

Open source · Apache 2.0

Manage every image, from upload to edge.

PhotonRelic is an open-source digital asset manager and image resizer that runs in your own Cloudflare account. Upload whole shoots, find anything, track rights and usage, and serve every size from the edge. Use both Workers together, or either one on its own.

View on GitHub

Public Worker

The resizer

Serves images from an R2 bucket at their own path and resizes them from a fixed menu. Output is cached at the edge, and anything off the menu gets a 400, which limits how many unique transformations anyone can request and so limits your Images bill. Give it read-only access to the library's database and it also honors focal points, answers a metadata API and stops serving images whose rights have expired.

GET /media/products/chair.jpg                      # as stored
GET /media/products/chair.jpg?w=768                # 768 px wide, format from Accept
GET /media/products/chair.jpg?w=320&ar=1:1&f=webp  # 320 × 320 cover crop, WebP
GET /api/v1/assets/media/products/chair.jpg  # alt text, credit, size, focal point
GET /api/v1/assets?tag=hero&limit=50         # a page, in key order

Crops that keep the subject

A cover crop is cut around the focal point set in the library. With no point set, Cloudflare Images picks the crop. Plain widths are never cropped.

A metadata API

Sites and build tools can read alt text, credit, dimensions, focal point and capture data as JSON, without reaching the private library. Hashes, the audit log and who filed what stay behind Access.

Expired rights stop serving

Once an image's rights expire, its URL answers 410 Gone. A copy already at the edge serves out its day, at most.

Control who embeds

Any site can embed by default. List your own hosts and pages elsewhere get a 403, which stops hotlinking. Every image served can be counted, too.

Private Worker, behind Access

The library

Browse the bucket, upload from the browser, and file images by subject, tags, alt text, credit and rights. Then find them again, see where they are used, and change them safely.

Upload a whole shoot

Drop a folder in the browser. Files are hashed and prechecked before any bytes move, then stream straight into R2. If the tab closes, drop the folder again and only the missing files go up.

Search everything you filed

Find files by path, subject, tags, alt text, credit, camera or lens as you type. Narrow by subject, tag, camera or state, then select the results and file them in bulk.

Track rights and expiry

Record a license, source, restrictions and expiry date on each file. An Expiring view shows what is about to run out, and a nightly run retires whatever already has.

Replace without breaking links

Put a reshoot or re-edit under the same key and every page that links to it picks up the change. With a cache purge configured, the edge updates within a minute.

Set a focal point

Click the part of an image a crop must keep: a face, a label, the product. Every crop the resizer cuts from then on is centered on it.

Keep capture data

Capture date, camera, lens and original size survive EXIF stripping, because the prep tool notes them first. GPS, serial numbers and owner fields are never kept.

See what is used

File pages show hits over the last 7 and 30 days, Retire and Delete warn when a file was served last month, and Unused lists what nothing has asked for in 90 days.

Copy the markup

Tick the widths, choose an aspect and sizes, and copy a picture element with avif and webp sources and a jpeg fallback, or a plain img with srcset.

Uploads never overwrite

An upload can't replace different bytes at the same key. Replace and Delete are deliberate acts on a file's page, and every write goes into an audit log.

File in bulk

Select tiles and set subject, tags, credit, license, expiry and an alt-text pattern together. You see a preview before anything is applied.

Find duplicates

Keys with identical bytes are grouped by R2 etag or SHA-256. Keep one and retire the rest; retired objects keep serving.

Fails closed

The Worker verifies the Cloudflare Access JWT itself on every request, so a misconfigured policy locks people out instead of letting them in. The bucket stays the record of what exists.

Run it yourself

Requires Node 22.13+, pnpm and a Cloudflare account with R2, D1 and Images. Workers Paid ($5/month) recommended.

pnpm install
pnpm test

Read the setup guide