safesearch=moderate isn't a Bing parameter. Bing's filter is set by the
SRCHHPGUSR=ADLT=OFF cookie, so send that (plus adlt=off).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Bing fallback, rate limit, fetch error and SurrealDB alerts now end with the
request URL (site, query, ?i= and ?src=), like New Search and Deduped already did.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A "Get Updates" form subscribes emails to the direct-img pool on
newsletter.planetrenox.com, behind the shared "newsletter" Turnstile
widget (direct-img.link was added to its allowed domains). The privacy
policy now covers what sign-up stores and the Turnstile script.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
A reload during a slow first-time search (seen on mobile Chrome and Safari)
made the runtime cancel the search mid-way. Its result was never stored and
its lock never released, so the reload waited out the 45s lock and then
served bad.webp. Logs show this as clientDisconnected invocations followed by
a bad.webp. The search now runs under waitUntil, so it still stores its
result and releases the lock, and the waiting reload serves the image.
Image candidates were downloaded one at a time, so each dead or slow host
could stall the search for its full 5s timeout. Up to 4 now download at once
but are checked in result order, so ?i= picks the same image. Downloads still
in flight are aborted once the image is found.
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>
Add _routes.json so /, /robots.txt and /assets/* skip the worker and
don't count against the Functions request quota. rate-limit.js moves to
assets/ so the homepage reads it client-side instead of via HTMLRewriter.
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>
- New logo (svg + jpg) and multi-size favicon.ico
- /favicon.ico still served at root so direct image views get the icon
- Generated missing bad.webp placeholder
- Moved limit.webp into /assets and routed error images via helper
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>