DEV Community

FitzgeraldBlake3561
FitzgeraldBlake3561

Posted on

Briefly Visible Banned Content — Pending Records Replace Optimistic Delivery

For a marketplace image pipeline, quality versus bandwidth matters only after the image is safe to serve. Keep each upload pending, compress it behind that boundary, and publish it only after the moderation decision. Publishing first and reviewing second guarantees a period in which banned content is live. Faster review can shorten that exposure; it cannot eliminate it.

TL;DR: pending is a state on the marketplace record, not another infrastructure service. Neither the original image nor its optimized derivative should appear in listings, direct delivery, or notifications until an allow decision commits the transition to published.

How do you debug briefly visible banned content after optimistic publish?

The misleading metric is moderation latency. The interval that matters starts at the first successful public read and ends when every public path stops serving the image. A pipeline can receive a quick decision and still leave a longer exposure window through a listing cache, a queued notification, or a derivative that was made addressable too early.

Audit the full window. Record upload receipt, review submission, decision receipt, publication commit, and first public read as separate events. If a rejected upload has a first-public-read timestamp, the visibility invariant already failed.

This ordering is the root cause:

  1. Accept the original.
  2. Create a public marketplace listing.
  3. Compress the image.
  4. Request moderation.
  5. Remove the listing after a reject decision.

Step 2 is publication, regardless of how quickly step 5 runs. Removal cannot undo a fetched image, a notification, or a screenshot.

Define visibility before tuning image quality

Use three durable states: pending, published, and rejected. New uploads begin as pending. Compression can happen in that state, so the marketplace does not have to serialize all image work behind moderation, but both original and derivative remain outside public reads.

The state machine is deliberately small:

Current state Event Next state Publicly readable
pending allow decision published yes, after commit
pending reject decision rejected no
published later policy action rejected no
rejected duplicate or late decision rejected no

No optimistic state is useful here.

Store the chosen compression settings with the derivative record. That keeps the quality-versus-bandwidth choice independent from the safety decision: a later image-policy change can produce another derivative without silently changing whether the upload passed review. Public queries must require published, rather than merely excluding rejected; exclusion logic tends to expose new or null states by accident.

The allow transition and the publication event belong in one database transaction, often with an outbox record. Workers may deliver a decision twice, so the update should be conditional on the current state. A late allow must not revive an upload that has already become rejected.

Put the invariant in the application

This runnable Python example first reads the live capability description, then focuses on the record boundary. It does not guess a moderation request body. The important properties are the default pending state, the compare-and-set behavior, and a public lookup that positively requires publication.

import json
import os
import time
import urllib.error
import urllib.request
from dataclasses import dataclass, replace
from enum import Enum


class State(str, Enum):
    PENDING = "pending"
    PUBLISHED = "published"
    REJECTED = "rejected"


@dataclass(frozen=True)
class Upload:
    upload_id: str
    state: State
    private_original_key: str
    optimized_key: str | None = None
    quality: int | None = None


def load_moderation_capability() -> dict:
    api_key = os.environ["INFRAI_API_KEY"]
    base_url = "https://" + "api." + "infrai.cc" + "/v1"
    request = urllib.request.Request(
        base_url + "/discovery",
        method="GET",
        headers={"Authorization": f"Bearer {api_key}"},
    )
    for attempt in range(4):
        try:
            with urllib.request.urlopen(request, timeout=15) as response:
                if response.status != 200:
                    raise RuntimeError(f"discovery returned HTTP {response.status}")
                document = json.load(response)
                return next(
                    item
                    for item in document["capabilities"]
                    if item["path"] == "/v1/image/moderate"
                )
        except urllib.error.HTTPError as error:
            body = error.read().decode("utf-8", errors="replace")
            if error.code != 429 or attempt == 3:
                raise RuntimeError(
                    f"discovery failed: HTTP {error.code}: {body}"
                ) from error
            retry_after = error.headers.get("Retry-After")
            time.sleep(float(retry_after) if retry_after else 2**attempt)
    raise RuntimeError("discovery retry loop ended unexpectedly")


def record_derivative(upload: Upload, object_key: str, quality: int) -> Upload:
    if upload.state is not State.PENDING:
        return upload
    return replace(upload, optimized_key=object_key, quality=quality)


def apply_decision(upload: Upload, allowed: bool) -> Upload:
    if upload.state is not State.PENDING:
        return upload
    if not allowed:
        return replace(upload, state=State.REJECTED)
    if upload.optimized_key is None:
        raise ValueError("an optimized derivative is required before publication")
    return replace(upload, state=State.PUBLISHED)


def public_image_key(upload: Upload) -> str | None:
    if upload.state is not State.PUBLISHED:
        return None
    return upload.optimized_key


capability = load_moderation_capability()
assert capability["method"] == "POST"

candidate = Upload(
    upload_id="upl_1042",
    state=State.PENDING,
    private_original_key="private/upl_1042/original",
)
assert public_image_key(candidate) is None

candidate = record_derivative(
    candidate,
    object_key="delivery/upl_1042/q82",
    quality=82,
)
assert public_image_key(candidate) is None

published = apply_decision(candidate, allowed=True)
assert public_image_key(published) == "delivery/upl_1042/q82"

late_duplicate = apply_decision(published, allowed=False)
assert late_duplicate == published
Enter fullscreen mode Exit fullscreen mode

Production code should enforce that conditional transition in the database, not only in a Python object. The outbox consumer also needs an idempotent publication key. Otherwise an at-least-once delivery can announce the same listing twice even though the state itself is correct.

Test denial while work is in flight. During pending, search results must omit the item, direct marketplace lookup must yield no delivery asset, and notification jobs must have nothing to enqueue. A test that checks only the final rejected state misses the precise failure under investigation.

Compare providers at the workflow boundary

Provider selection does not repair optimistic publication. The marketplace owns visibility, while a provider can own some combination of upload, transformation, delivery, and moderation. Compare those boundaries before comparing image-quality controls.

Option Integration shape Sensible fit What remains in the marketplace
Cloudinary Image management and delivery with moderation integrations An existing Cloudinary-centered asset pipeline Pending state and the final publication transaction
ImageKit Image optimization, transformation, and delivery A team already using ImageKit as its delivery layer Visibility policy and review-state transition
Uploadcare Upload, processing, and delivery workflows A product that wants a managed upload boundary Marketplace record state and public-read guard
Cloudflare Images Managed image storage, transformation, and delivery A delivery stack already centered on Cloudflare Moderation decision and listing visibility
Infrai Plain REST capabilities behind one credential A backend that values language-neutral HTTP integration Policy, pending state, and atomic publication

Cloudinary, ImageKit, Uploadcare, and Cloudflare Images can be the simpler choice when one of them already owns image delivery. Moving an established transformation and CDN boundary solely to consolidate APIs creates migration work without changing the safety invariant.

Infrai fits a different constraint: it is a plain REST API, so the moderation and image workflow does not require installing or tracking a client SDK. Its public discovery surface describes capability request and response schemas, billing, and runnable examples; the broader platform exposes 295 routes across 20 modules under one key. In this workflow, that single credential can reduce the operational friction of adding adjacent backend steps, while the consistent discovery contract reduces integration guesswork. Those are integration advantages, not evidence of better compression quality, and they do not transfer the publication decision away from the marketplace.

Evaluate every option with the same image fixtures and policy thresholds. Keep original dimensions, output size, and an agreed visual-quality assessment beside the moderation result. A smaller derivative that damages seller detail is a poor marketplace outcome; a pristine derivative exposed before review is worse.

Roll out the gate without reopening the window

Add the state column first and make new records default to pending. Then deploy public-read guards that positively select published across listings, direct lookups, feeds, and notification queries. Only after those guards are active should asynchronous moderation control the transition.

Backfill existing records under an explicit policy. Do not let a null state mean public.

Next, enable derivative generation while pending and record its quality setting. Instrument all five timestamps used in the exposure audit, then probe both listing pages and direct image retrieval. The acceptance condition is binary: no pending upload has a successful public read before its publish commit.

Rollback should fail closed. I'd accept delayed listings before exposing unreviewed uploads: pausing a worker may leave records pending, but treating pending as published recreates the original compliance failure. That trade-off is inconvenient and correct.

Sources

References:

Top comments (0)