They were kept for 24h so a later search could replace them, but that
regenerated them daily for no real gain. Searches where the AI image also
fails are still remembered for 24h as bad.webp. With one TTL for all images
again, the per-image ttl stored in KV and R2 is no longer needed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ai.direct-img.link serves images from the AuroraIMG-6M API on our Dokploy
server (github.com/direct-img/AuroraIMG-6M) instead of searching. It takes
?src= (auroraimg-6m, case-insensitive, the default and only source for now)
and ?i=, where each i gets its own seed derived from the query, so it's a
different image of the same prompt. It has its own daily limit
(AI_DAILY_LIMIT) and AI: cache keys / ai/ R2 prefix. Failed generations
aren't cached, since they're likely temporary.
On direct-img.link and free.direct-img.link, a search that finds nothing (or
fewer than i working images) now serves an AI image of the query instead of
bad.webp. It's kept for 24h like failures were, so a later search can still
find a real image; bad.webp is only served if the AI image fails too. Its ttl
is stored in KV and R2 metadata so cache hits and deduped waiters send a
matching max-age.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Two misses for the same query at once each ran their own search (ntfy
showed Search #24 and #25 for one image). An in-memory fix wouldn't
hold: a test of concurrent same-query requests on this project found
they share an isolate only ~25% of the time.
The rate-limit query is now one transaction that also claims a
per-query lock, so it's still a single SurrealDB round trip. Requests
that find the lock held write nothing (so aren't counted), poll until
it's released, then serve the image from R2. Write conflicts roll the
whole transaction back and are retried.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Limits change (e.g. 35 -> 25/day) and the README kept going stale, so it
now links to the homepage, which reads them live from rate-limit.js.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Free searches never spend Brave credits, so they no longer share the
main site's 35/day counter. Main-site rate ids are unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Scanner probes like /userfiles?path=../../.env were treated as real
searches since extra params were ignored, wasting searches and rate limit.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- free.direct-img.link serves only unrestricted (public domain/CC0) images
from Openverse (license=cc0,pdm) and Wikimedia Commons
(haslicense:unrestricted), with optional ?src=openverse|wikimedia
- ?i=1..20 picks the i-th working image on both sites; broken links are
skipped so every index is a different image
- Openverse OAuth token is cached in KV and refreshed automatically
- Skip Flickr "photo unavailable" placeholders (served with HTTP 200)
- Free/indexed cache keys use uppercase prefixes; plain main-site keys
are unchanged so existing cache entries stay valid
- Update README, homepage, privacy policy and terms
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
- Update AI system prompt example (README + index)
- Add copy button for the prompt on the index page
- Add GoatCounter to homepage; report image hits server-side via
waitUntil (optional GOATCOUNTER_URL/GOATCOUNTER_TOKEN)
- Add TERMS.md and PRIVACY.md, linked in the index footer
- Remove dead c.planetrenox.com tracker
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>