Skip to content

Storage images for Next.js: public CDN delivery, private authorization and CLI onboarding - #500

Open
kvz wants to merge 80 commits into
mainfrom
img-onboard
Open

kvz wants to merge 80 commits into
mainfrom
img-onboard

Conversation

@kvz

@kvz kvz commented Sep 12, 2026

Copy link
Copy Markdown
Member

Why

Store one original, commit its catalog, and import StorageImage from the package.
No image init or generated application factory on the default path; responsive image bytes
go directly from Smart CDN to the browser, never through Next's image optimizer.
This consolidates #497#499.

What

  • storage store --public explicitly publishes the destination directory (including future
    objects), checkpoints the receipt, and generates transloadit-images.d.ts for typed paths.
  • storage store --hashed inserts eight input-MD5 hex digits before the extension. Catalog keys,
    generated types and printed JSX use that exact name; source retains the local basename.
    Same-workspace/API-origin receipts with matching full MD5 and size skip repeat uploads.
    Changed bytes get another name; short-hash conflicts never overwrite. Keep the committed catalog.
  • StorageImage from @transloadit/img/next, bound by one withTransloaditImages() config wrapper.
    The plugin bundles the catalog; no cwd-based zero-config runtime fallback is promised.
  • Public images need no app secrets. Private delivery uses transloadit.authorize.ts, a package
    route re-export and a separate application signing key. Authorization runs on image requests;
    redirects return no image bytes. Explicit factories and opt-in scaffolding remain available.
  • Responsive layouts/art direction, native format selection, preload, visible error recovery,
    CLI browser login, publication and atomic catalog recovery remain covered.
  • Store saves optional ThumbHash and alpha metadata. Server-decoded placeholder="blur" needs no
    client load handler; alpha, letterboxed and private redirect images omit retained blur pixels.
  • Candidate URLs omit the pinned Built-ins' verified defaults; custom Templates keep explicit
    parameters. This deliberately changes cache keys before release. Private signatures cover the
    final query; public outputs respect the Built-in's 4096px / quality-85 limits.
  • v remains an unverified cache-busting tag, not object versioning. Prefer --hashed for images
    you will replace. Older servers that transform uploaded bytes cannot treat an unchanged local
    original as a matching replay; the diagnostic now explains that hard failure.
  • Unavailable S3 recovery commands live in the explicitly gated reference, not the Quickstart.
    The README and post-write CLI error distinguish committed-catalog/Console recovery from the
    Storage read API that is not yet enabled in production.

Refs #270

Implements browser/code auth login, credentials in ~/.transloadit, --no-browser and
--stdin fallbacks. Still open: npm create transloadit-scaffold and Playwright E2E of the actual
Console login/approval page. Refs, not Fixes.

Verification and rollout

DX documentation follow-up: 13a239c, exact-head CI.
Green: all ten applicable jobs, including the packed Next browser fixture; three reporting-only
jobs are skipped by their existing conditions. Local yarn check, yarn verify and strict
TypeScript checking of the new server-upload snippet pass. CLI-first signup, the discoverable
storeImage() recipe and the parent-constrained fill example address the fresh independent DX
review. Runtime code and dependencies are unchanged; the legacy README was regenerated.

A separate live Uppy parameter-builder → local Assembly → stored-byte probe confirms all three
collision policies: omitted policy overwrites, error preserves the original and rejects the
second write, and rename preserves both. Five helper tests pass. No Uppy default was changed;
application-image recipes use server-owned unique paths and explicit conflict refusal. This is
local contract evidence, not hosted delivery or browser/Companion coverage.

Round 15: 945c798, exact-head CI.
Green: all ten applicable jobs, including fast/full verification, Node 20/22/24, E2E,
package build, release dry run and the packed Next fixture. Local final verification passes
319 img / 636 Node tests (+ one existing skip), root 30 / utils 59 / MCP 3 / notify 22.

Downloaded exact-head browser artifacts were independently audited: 122 first-attempt passes
(56 cacheComponents enabled, 56 omitted, 10 development), zero retries/skips/flakes/unexpected
errors. The eight new hashed-image cases decode the catalog path at desktop/mobile widths in
Chromium and WebKit. Local packed tests also pass all 122 cases on functional head ff776b6;
the final CLI-message-only commit is covered by final local verification and exact-head CI.

Red-first coverage protects filename placement, catalog/types/JSX agreement, repeated and changed
uploads, complete-checksum/size validation, API-origin binding, explicit-workspace batch dedup,
path-length limits and refusal to overwrite. Council's four valid findings were fixed with tests;
Opus's focused post-fix review confirms them. Its last adjacent recovery-message caveat was also
fixed red-first. Generated legacy documentation was regenerated, not hand-edited.

The first packed attempt exposed test isolation: changing login files in one cached CLI cwd was
correctly treated as untrusted. The corrected seed uses its own app cwd, retains the ownership
check, and copies its verified catalog/types into the browser app. No production auth change.

Owned live Next desktop/mobile and exploratory evidence confirms native hashed delivery,
one Assembly for repeated identical uploads, unsigned public HTML/Flight, no console/page/network
errors and retained SPA state. Local contract fakes are not hosted or production measurements.

No merge/publication: img remains private at 0.0.0. Coordinate API2/Console rollout, ordinary
registry installation, sustained Content dogfood and production Bunny measurements before release.

Known limits

  • The existing generic @transloadit/utils/node getSmartCdnImageCandidates public-template
    height-limit bug remains explicitly deferred. StorageImage does not use this helper and
    enforces its own public limits.
    Correct that generic helper before advertising it for the
    public Built-in; reproduction and the separate scope are in the living plan.
  • Production S3 read availability, hosted Vercel/standalone deployment, registry install and
    actual Bunny cache/cost measurements remain rollout gates. Whole-query caching is configured
    on *.tlcdn.com, not a universal Bunny default. Edge runtime is unsupported.
  • Private delivery needs a server-only application key with Smart CDN enabled and smart_cdn:sign
    (assemblies:write also accepted), plus application session/per-object authorization.
  • Hashed replay trusts a committed, matching upload receipt; it does not probe the remote object.
    It fails closed for absent/different API provenance and mismatched or transformed bytes.
  • Blur is best-effort, adds up to about 6 KB before HTML compression and requires img-src data:.
    Sharp is optional; encoding is bounded by 32 MiB input, 40 MP, a 100×100 hash input and a two-second
    decoder timeout. Fresh List + HEAD recovery cannot reconstruct missing ThumbHash/alpha metadata.

Quickstart
· Reference
· Living plan

@kvz kvz changed the title Complete the Storage seed-to-image recipe and SDK catalog Complete Storage images in Next.js: native props, layout, lifetime and onboarding Sep 12, 2026
@kvz
kvz changed the base branch from img-lifetime to main September 12, 2026 08:45
kvz added 15 commits September 12, 2026 12:05
Pin storage-preview 0.0.2 and bind per-format backgrounds through direct URLs and authorized redirects. Keep JPEG opaque and make its color explicit.

Sync the canonical recursive import schema from API2 #9057 and document the confirmed cache-key and two-key contracts. Cover alpha and fallback pixels in the packed browser fixture; record the separate live canary DNS limitation.
Address the five final round-3 council findings: retain receipt-file errors, print safe post-write recovery details, clarify invalid fixed sources and subpixel crops, and remove the stale import-extension prerequisite.

Reproduce all findings red-first, then verify affected packages, legacy parity, the full workspace and all 48 packed browser cases before pushing.
kvz added 30 commits September 14, 2026 01:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant