MainSDK
Search⌘K
Integrations

Vercel Blob

Vercel Blob as the planned MainSDK files driver for apps on Vercel, with setup, main.yaml, a direct upload example and how privacy is kept.

1 interface1 driverWritten guide
Updated 2026-10-28

Vercel Blob is object storage on Vercel. The vercel-blob driver for files keeps your uploads next to your app and adds owner scope, scanning and short-lived URLs on top. You would pick it when your app already runs on Vercel and you want no separate bucket to manage.

Interfaces it serves

Pick Vercel Blob in main.yaml for any of these. Your calls stay the same, and you can swap later.

Before you start

  1. Create a Blob store in your Vercel project.
  2. Keep its read-write token as BLOB_READ_WRITE_TOKEN in your secrets manager or Vercel environment, never in code.
  3. Deploy to Vercel with the guide in Deploy on Vercel.

Configure

main.yaml
files:
  driver: vercel-blob
  key: secret:BLOB_READ_WRITE_TOKEN
  scan: true
  max-size: 25MB

Use it

Issue an upload on the server. The browser sends the file straight to the store, which also covers files larger than a serverless request body allows.

app/api/uploads/route.ts
import { auth, files } from "mainsdk";

export async function POST(req: Request) {
  const session = await auth.require(req);
  const upload = await files.uploadUrl({
    owner: session.user,
    maxSize: "25MB",
    types: ["application/pdf", "image/png", "image/jpeg"],
  });
  return Response.json(upload);
}
app/api/uploads/[id]/complete/route.ts
import { auth, files } from "mainsdk";

export async function POST(req: Request, { params }: { params: Promise<{ id: string }> }) {
  const session = await auth.require(req);
  const { id } = await params;                           // upload.ref.id
  const ref = await files.complete(id, { owner: session.user }); // only the uploader can finish it
  const url = await files.url(ref, { for: session.user, expiresIn: "1h" });
  return Response.json({ url });
}

The browser sends the file to upload.url with upload.method. The browser function, the Hono version and the offline test are in Upload files with S3, Uploadthing or Vercel Blob.

A public blob URL is never returned for a private file. If the store can only serve a file on a public URL, the driver declares that gap in its capability profile and mainsdk check names it. Whether the driver can keep a blob private is not defined yet.

Local development and testing

Use the local driver in development and createTestApp() from @mainsdk/testing in tests. Neither needs a Blob store.

What MainSDK adds on top

  • Owner-scoped reads and short-lived URLs on every file.
  • A scan before a file is readable, and idempotent uploads.
  • mainsdk check fails if route code imports @vercel/blob directly; files.raw() gives you its client and is listed as outside the guarantees.

Verify

Run npx mainsdk check --env prod. A pass exits 0 and names no MAIN-FILES or MAIN-CONFIG finding.

If it fails

CodeCauseFix
MAIN-FILES-0002files.url() was called for a reader access denies (403).Read as the owner, or add an access rule for the reader.
MAIN-FILES-0005The scan in files.complete() found an infected file. The upload fails and the file is quarantined.Ask the user for a clean file. Nothing to fix in code.
MAIN-FILES-0010mainsdk check found @vercel/blob imported in route code.Use files.uploadUrl() and files.complete(), and keep the store as the files driver.
MAIN-CONFIG-0004key is a literal value, not a secret: reference.Move the value to your secrets manager and write secret:BLOB_READ_WRITE_TOKEN.

For agents

text
Goal: use vercel-blob as the files driver and add owner-scoped uploads to this app.
Read: docs/specs/files-share.md, this page, main.yaml.
Touch only: main.yaml, main.env.dev.yaml, app/api/uploads/**, one test file.
Do: set files.driver to vercel-blob with key: secret:BLOB_READ_WRITE_TOKEN;
issue uploads with files.uploadUrl({ owner, maxSize, types });
send the file to upload.url with upload.method; call files.complete(upload.ref.id, { owner }) as the same user, then files.url(ref, { for, expiresIn }).
Verify: npx mainsdk check --json --agent
Never weaken, skip or loosen a check to make it pass.
Never import @vercel/blob in route code.

Swap it

Change driver to s3, uploadthing or getqr. Your calls stay the same. See Swap a driver.

Open questions

Was this page helpful?

↑↓ moveEnter openEsc close

Not decided yet