imgd.dev returns the final public URL as soon as it accepts an upload.
While the status is processing, that URL serves a strong blurred placeholder.
Unreviewed recognizable bytes are not served.
The temporary processing state can change automatically, but terminal review does not clear without a person.
Every imgd.dev image is public. A blurred state is not private storage. Do not upload private, confidential, or secret material.
The state model
| State | Public URL behavior | Automatic transition | Cache behavior |
|---|---|---|---|
processing |
Strong blurred JPEG placeholder | Yes | no-store |
live |
Original or requested transform | No further moderation transition | Public cache policy |
review |
Blurred image with a review header | No. This state is terminal. | Public cache policy |
blocked |
404 JSON error |
No | no-store |
error |
404 JSON error |
No | no-store |
A caller must not use review and processing as synonyms.
Only processing means that the automated check still has work to do.
Current implementation facts
A new image follows this path:
- The upload route validates authentication, quota, size, and media type.
- It computes the SHA-256 content hash.
- It writes new bytes under a quarantine object key.
- It writes image metadata with
status = processing. - It sends a moderation queue message.
- It returns HTTP
202with the final image URL.
The public serving route checks the database state before it reads a public object.
For processing, it reads the quarantined bytes and applies the strongest supported blur.
It returns JPEG bytes with this header:
X-Imgd-Moderation: processing-blurred
The response also uses:
Cache-Control: no-store, no-cache, must-revalidate, max-age=0
Retry-After: 3
The processing placeholder never enters the Worker cache.
The test suite checks that no cache.put call exists in this branch.
Repository evidence:
worker/index.tsworker/moderation.tsworker/lib.tstests/moderation.test.mjs- Git commit
b0d8d093313a5fca0b7eff3ffa15d4982b68bd0b
Promotion after the check
The moderation consumer reads the quarantine object and creates the moderation thumbnail.
It then selects live, review, or blocked through the current confirmation rules.
For live and review, the worker performs these operations:
- Write the original bytes to the internal public object path.
- Delete the quarantine object.
- Store the final status and generated alt text.
A live request can serve the original bytes or a requested transform.
A review request always applies the review blur before it serves an image.
The response includes:
X-Imgd-Moderation: review-blurred
For blocked, the worker deletes the quarantine bytes.
The public URL then returns 404 and a contest instruction.
Measured production facts
This page does not claim a moderation latency percentile or a cache-hit rate. The current repository records state behavior in code and tests, not a timing sample for this page.
The article therefore separates implementation facts from design rationale.
Do not infer a fixed moderation duration from Retry-After: 3.
That header tells a JSON caller when to try another status request.
Design rationale
The URL must work immediately for documents, pull requests, and agent messages. A broken URL forces every caller to build a wait-before-embed workflow.
The placeholder design keeps the final URL usable without exposing recognizable unreviewed content. The processing blur is stronger because no moderation result exists yet. The review blur is less strong because an automated check already placed the image in a non-destructive hold.
The no-store policy is essential during processing.
A cached placeholder could remain blurred after the image reaches live.
The serving path therefore forbids storage of that temporary response.
The JSON branch supports programs that need state instead of image bytes. It preserves machine-readable polling without breaking normal image clients.
Image clients and JSON clients see different processing responses
A normal image client requests the URL without an explicit JSON preference.
On the normal processing path, it receives HTTP 200 image bytes for the blurred placeholder.
A status client sends Accept: application/json to the same URL.
During processing, it receives HTTP 409:
{
"error": "moderation_pending",
"fix": "this image is still clearing moderation; the same url already serves a blurred placeholder to image clients, and sharpens once the check completes",
"status": "processing"
}
The URL and hash do not change when the image reaches live.
The response type changes because the stored state changes.
Practical verification procedure
imgd.dev charges a one-time $1 per GB storage price and has no free tier. Use an existing funded account and a synthetic public image.
Upload the image:
curl -X POST https://imgd.dev/v1/upload \
-H "Authorization: Bearer $IMGD_KEY" \
-F "file=@public-sample.png"
Save the returned hash and url without storing the key in a log.
Fetch the image response immediately:
curl -sS -D /tmp/imgd-headers.txt -o /tmp/imgd-body.jpg "$URL"
grep -iE 'HTTP/|content-type|cache-control|x-imgd-moderation' /tmp/imgd-headers.txt
If the state is still processing, expect an image response and the processing-blurred header.
The cache header must include no-store.
If moderation already finished, expect the final state response instead.
Request JSON state from the same public URL:
curl -i -H "Accept: application/json" "$URL"
During processing, expect HTTP 409, Retry-After: 3, and moderation_pending.
After the state changes, the public URL can return image bytes instead.
Check owner metadata:
curl "https://imgd.dev/v1/images/$HASH" \
-H "Authorization: Bearer $IMGD_KEY"
Stop on live, review, blocked, or error.
Do not poll a review result because it is terminal.
Upload recovery rules
401: correct the missing or invalid key before another request.402: buy storage or delete stored images before another request.413: resize the image before another request.415: convert the image or correct its media type before another request.429: wait before a bounded retry.
Do not retry every 4xx response.
Read how actionable API errors help agents recover for the full branch policy.
Security rules
- Treat every returned URL as public.
- Remove secrets and personal data before upload.
- Keep
$IMGD_KEYoutside URLs, source files, and logs. - Do not use the processing blur as a privacy control.
- Stop on terminal states.
- Use the human contest path for a review or block that appears wrong.
When this design fits
This design fits public-image workflows that need a stable URL while moderation runs. It fits image clients that can accept a temporary blur and agents that can poll the same URL. imgd.dev does not fit private images, a free trial, video, or full digital-asset management. Use another service when an image needs access control.
Read the terminal screenshot moderation incident for the confirmation rule. Read the moderation thumbnail size decision for the 768-pixel production edge.
For an agent-led setup, use /integrate.md.
For a human setup, open Get started.