TL;DRNaly uses Vercel Blob as the publication boundary for generated article media: cover images and social images are produced by the media pipeline, uploaded as public blob objects, and then their resulting URLs are written back to article rows. That turns a probabilistic generation step into a durable asset contract for hero views, article cards, and Open Graph previews, so rendering can read stable URLs instead of recreating images.
Abstract
The thesis is that generated media should be committed once, then served many times. For Naly, Vercel Blob is not only a file bucket. It is the handoff point where AI-generated image bytes become article infrastructure: named, cached, URL-addressable, and referenced from database state. The system design problem is therefore less about how to make a good image and more about how to prevent image drift after publication.
This matters because article media participates in acquisition surfaces. A reader may first see a Naly article through a social card, a search preview, an AI citation surface, or an internal article grid. If those surfaces disagree, the article feels less trustworthy. A public blob URL stored on the article row gives every renderer the same asset identity.
Where it sits in Naly
Naly stores generated cover images and social images in Vercel Blob, then writes the public URLs back to article rows for hero, card, and Open Graph surfaces. In the current stack, that boundary sits between media generation workers and the Next.js 16.0.7 rendering layer. React 19.2.1 renders the article surfaces, Drizzle ORM 0.44.x and Neon serverless persist article state, and @vercel/blob 2.x provides object storage.
The article row should be treated as the source of truth after media promotion. Rendering code should not ask whether an image needs to be generated. It should read the stored URL, dimensions, alt text, and media kind from publication state. Next.js generateMetadata can then map the same URL into openGraph.images, while the article page and card component use the identical stored field.
Open Graph gives the operational reason. The protocol defines og:image as the image URL representing the object, and recommends structured image fields such as width, height, type, and alt text. For Naly, the article is the object. The blob URL is the media identity attached to that object.
Technical mechanism
Vercel Blob is object storage for files uploaded at build time or runtime. The official use cases include cover images, screenshots, videos, and other public media. Blob stores are created as public or private, and that choice is important because public article images need direct crawler access. A private blob can be correct for sensitive user data, but it is a poor fit for Open Graph images because social crawlers and preview bots usually need an unauthenticated URL.
A typical Naly media path is:
- Generate image bytes for an article-specific purpose, such as cover or social preview.
- Attach metadata before upload: article id, media kind, prompt version, model version, dimensions, MIME type, and a content hash.
- Upload with
putfrom@vercel/blob, usingaccess: publicand a unique pathname oraddRandomSuffix. - Receive the blob result, including
url,pathname,contentType,downloadUrl, andetag. - Persist the public URL and supporting metadata back to the article row.
- Render hero, card, and Open Graph surfaces from that persisted state.
The important invariant is commit-once media. A generated image is not production media while it only exists in memory, a temp file, or a model response. It becomes production media when the blob upload has succeeded and the article row records the resulting URL.
Vercel Blob caching reinforces this design. Public blob URLs are cached by Vercel CDN and by browsers for up to one month by default. The Vercel docs explicitly recommend treating blobs as immutable to avoid stale-cache behavior after overwrites. That recommendation maps directly to Naly: never overwrite a published cover or social image path. If an image must change, create a new blob path and update article state as an explicit media revision.
Server upload mechanics also constrain the design. Vercel documents a 4.5 MB request body limit for Vercel Functions on server upload routes. For generated images created inside a worker, the incoming HTTP body limit may not be the binding constraint, but function memory, duration, image dimensions, and upload size still matter. Larger generated assets should be compressed, dimensioned deliberately, or moved through an upload path that does not put avoidable pressure on request bodies.
What the literature says
The image-generation literature explains why persistence is necessary. Latent Diffusion Models showed that high-resolution synthesis can be made practical by operating in latent space rather than raw pixel space, reducing computational cost while preserving visual quality. That made generated article images operationally plausible, but it did not make regeneration stable. Prompt wording, model version, seed handling, and provider behavior can all change over time.
Recent provenance work points in the same direction. SynthID-Image describes watermarking AI-generated imagery at internet scale and frames deployment around effectiveness, fidelity, robustness, and security. The lesson for Naly is not that every cover image must rely on one watermarking scheme. The lesson is that generated media needs explicit provenance fields because the image alone is an ambiguous artifact.
News media provenance research is especially relevant. The News Media Provenance Dataset paper argues that out-of-context and misattributed imagery is a leading form of media manipulation, and that location and date relevance remain difficult. Naly cover images are editorial assets rather than primary evidence, but they still need context. A generated image should be attached to the article, date, and generation record that produced it, not allowed to float as an unexplained media file.
The 2026 work on authenticated contradictions between provenance metadata and watermarking is a useful warning. It shows that independent authenticity layers can pass their own checks while disagreeing semantically. For Naly, the practical response is cross-layer consistency: the article row, blob object metadata, content hash, alt text, generation manifest, and rendered metadata should all describe the same asset.
Design trade-offs
Public blob storage is the right default for published article media because it gives direct URLs for browsers, social crawlers, and Open Graph consumers. The trade-off is exposure. Anyone with the URL can read the asset, and Vercel notes that public blob URLs can be indexed if linked or embedded in public pages. Naly should therefore store only publishable assets in public Blob stores.
Immutable paths cost more storage than overwriting, but they avoid a worse class of bugs. Overwrites create ambiguity: one URL may refer to different bytes at different times, while caches continue serving older content. Immutable paths make the database update the only promotion step that changes what readers see.
Blob-hosted media also competes with dynamic Open Graph image generation. Next.js can generate Open Graph images with file conventions and ImageResponse, and those generated routes can be cached. That is useful for deterministic templates. But for AI-generated cover art, persistent blobs are cleaner because the expensive, probabilistic work happens before publication, not during crawler access.
Finally, URL-as-state is simple but incomplete. A URL tells renderers where to fetch the image. It does not prove which model made it, which prompt produced it, or whether it matches the article. Naly should store provenance metadata beside the URL, even if only the URL is used in the public renderer.
Failure modes
- Regeneration drift: a job reruns with a different model, seed, or prompt and silently changes the article image. The mitigation is to store the published blob URL and never regenerate during render.
- Cache incoherence: a blob path is overwritten, but CDN or browser caches keep serving old bytes. The mitigation is unique pathnames and no
allowOverwritefor published media. - Partial commit: upload succeeds but the article row update fails, leaving an orphan blob. The mitigation is an orphan cleanup job keyed by upload time and absent article reference.
- Broken promotion: the article row points to a URL that was never uploaded or is not public. The mitigation is a post-upload validation check before publish.
- Preview mismatch: hero, card, and Open Graph fields read different media fields. The mitigation is one article media contract with named variants and shared metadata.
- Oversized assets: image bytes exceed practical upload, cache, or social-preview limits. The mitigation is fixed dimensions, compression, MIME validation, and size budgets before upload.
- Public leakage: a non-public draft or sensitive artifact is uploaded to public storage. The mitigation is separate stores or prefixes for draft and published assets, with explicit promotion.
- Provenance split: blob metadata, article row, watermark, and rendered alt text describe different things. The mitigation is to persist a generation manifest and audit it against rendered output.
Implementation notes
Use pathnames that encode intent without depending on overwrite behavior. A good pattern is articles/{articleId}/{mediaKind}/{revision}-{contentHash}.png, or a stable prefix plus addRandomSuffix. The prefix makes cleanup and debugging easier; the suffix or hash makes collisions unlikely.
Store more than the public URL. At minimum, the article row or a related media table should keep pathname, url, etag, contentType, width, height, alt, mediaKind, contentHash, and createdAt. For generated media, add prompt version, model version, and generation job id. These fields let Naly answer what changed when a preview changes.
Do not wrap Blob upload and database update in a fake transaction. Blob storage and Postgres cannot share one atomic commit. Use a small state machine instead: generated, uploaded, validated, attached, published. If the process stops after upload, the blob is recoverable as an unattached artifact. If it stops after attach, renderers can already use the URL.
Keep render-time behavior boring. The article page should render the stored cover URL. The card should render the stored card or cover URL. generateMetadata should render the stored social URL into Open Graph metadata. Any fallback image should be explicit and observable, because fallback use means the media pipeline failed or has not run.
Treat media revision as editorial state. If a better image is produced later, create a new blob and update the article media fields as a revision. Keep the old blob long enough for external caches and historical audits. That turns asset replacement into a controlled publication event rather than an invisible mutation.
References
- Vercel Blob Overview
- Vercel Blob Server Uploads
- Vercel Blob Public Storage
- Vercel Blob SDK
- Next.js generateMetadata
- The Open Graph protocol
- High-Resolution Image Synthesis with Latent Diffusion Models
- SynthID-Image: Image watermarking at internet scale
- Dataset of News Articles with Provenance Metadata for Media Relevance Assessment
- Authenticated Contradictions from Desynchronized Provenance and Watermarking