Skip to content
Pinner.xyz

Upload CAR Files

CAR (Content Addressable Archive) files let you upload IPFS data with the CID already known before the upload even starts. This is useful for bulk uploads and when you need to guarantee the CID ahead of time.

Not sure what CAR files are or why you'd use them? See CAR Files.

Upload a CAR file

import { Pinner } from "@lumeweb/pinner";
import fs from "fs";
 
const pinner = new Pinner({ jwt: process.env.PINNER_AUTH_TOKEN! });
 
const carData = fs.readFileSync("./data.car");
const carFile = new File([carData], "data.car", {
  type: "application/vnd.ipld.car",
});
 
const operation = await pinner.uploadCar(carFile);
const result = await operation.result;
console.log("Root CID:", result.cid);

ReadableStream input

uploadCar also accepts a ReadableStream<Uint8Array>, which is useful in browser environments or when streaming CAR data without buffering the entire file:

const stream: ReadableStream<Uint8Array> = getCarStream();
 
const operation = await pinner.uploadCar(stream, {
  name: "data.car",
  size: carByteLength,
});

When providing a ReadableStream, set name and size in the options so the upload handler can choose the right protocol (XHR vs. TUS resumable) and apply the correct MIME type.

Marking regular uploads as CAR

If you use the generic pinner.upload() method (not uploadCar), you can pass isCarFile: true in the options to skip CAR preprocessing and upload the file as-is:

await pinner.upload(carFile, { isCarFile: true });

This is useful when you already have a CAR file and don't want the SDK to re-process it into another CAR.

Why CAR instead of a regular upload?

With a CAR file the root CID is deterministic; it's computed from the IPFS block structure before the upload begins. You can verify the CID locally and know it won't change after pinning.

Regular uploads also work fine. CAR is for cases where you need the CID upfront: deterministic builds, verification pipelines, or bulk transfers of pre-packaged IPFS data.

Next steps