How it works

One API client, three galleries

This site keeps no images of its own. They're all in one Pixel Sentinel account (skeeter@pixelsentinel.net), and every page asks the Pixel Sentinel Client API for them while it renders.

  1. The site's server gets a token

    It runs on Cloudflare Workers with an API client's ID and secret as secrets. It swaps them for an access token at POST /oauth/token (OAuth 2.0 client credentials) and reuses the token for an hour. The browser never sees either.

  2. It lists the images for the page

    Pixel Sentinel has no folders, so the gallery is part of each image's name: photo--harbor-at-golden-hour.jpg. A gallery page is GET /client/v1/images?q=photo--. Open “API calls” at the foot of any page to see exactly what it asked for.

  3. The page carries scrambled images only

    Each picture is an <img> pointing at the scrambled copy, marked with data-pg. That's all a crawler, an AI scraper or “Save image as…” ever gets.

  4. embed.js draws the real picture

    Pixel Sentinel's loader script asks for a one-time, freshly shuffled copy of each image and reassembles it on a canvas over the <img>. Pixel Sentinel only answers when the page's site is on the account's approved list, so the same HTML copied to another site shows scrambled pictures.

What each page calls

PageAPI callsScopes
HomeGET /client/v1/images?q=photo--&limit=6 (and art--, graphics--)images:read
A galleryGET /client/v1/images?q=art--&limit=24&offset=…images:read
An imageGET /client/v1/images/{id}images:read
UnprotectedGET /client/v1/images?q=open-- (the pictures are plain files on this site)images:read
CompareGET /client/v1/patrols, then GET /client/v1/patrols/{id}patrols:read
Manage: overviewGET /client/v1/account, PUT /client/v1/domainsaccount:read, domains:write
Manage: imagesGET, POST /client/v1/images; DELETE …/{id}; POST …/{id}/download-linkimages:read, images:write, links:write
Manage: generate with AIWorkers AI (FLUX.1 schnell), then POST /client/v1/imagesimages:write
Manage: trafficGET /client/v1/traffic?days=…&image=…traffic:read
Manage: patrolsGET, POST /client/v1/patrols; GET …/{id}; GET …/{id}/report.csvpatrols:read, patrols:write

The images

All of them were made with FLUX.1 schnell on Cloudflare Workers AI from the prompts in scripts/prompts.json, then uploaded with the Python sample's seed.py. The manage area can generate more and uploads them the same way.

See the code