Introducing storagesdk.dev | Tigris Object Storage
You've spent fifteen years branching, tagging, and rebasing your code without thinking twice. Then you reach for object storage and the API is PUT, GET, LIST, DELETE, and "good luck." Buckets are the one piece of your stack you can't safely fork, can't tag a known-good state on, can't mutate on a side branch without a sinking feeling.
StorageSDK is what happens when you decide that's a bug. It's a TypeScript SDK for object storage with snapshots and forks as first-class primitives: branch a bucket per agent run, mutate safely, replay from the same baseline. Same API on top of whichever backend you pick.
Today I'm excited to announce storagesdk, built in partnership with @TigrisData
A TypeScript SDK for object storage with snapshots and forks as first-class primitives
branch a bucket per agent run. mutate safely. replay from the same baseline. Across many providers.
The other half of the picture
ComputeSDK is a vendor-neutral project that gives you one consistent API to control sandboxes across every major sandbox provider. We've partnered with the ComputeSDK team to launch StorageSDK as the matching half — same shape, same goal, but for storage instead of compute. Pick a backend, get out of your way.
The thesis underneath both: Git === storage. Not literally. Conceptually. Buckets need what Git gave us fifteen years ago — tags, branches, isolated mutations, replayable state. Once you've spent a career with those primitives in your VCS, going back to a flat object store feels like editing without undo.
The GitHub adapter is where the metaphor stops being a metaphor:
storage.snapshots.create() // makes a tag
storage.forks.create() // makes a branch
storage.forks.get(name) // hands you a writable storage handle on that branch
The rest of this post is about applying the same idea to every other backend.
One interface. Many backends.
StorageSDK gives you one interface across basic operations that are portable across every storage provider: get, put, list, delete, copy, and make a URL. Changing backends is one import and one adapter config — swap the tab to see for yourself:
- Tigris
- Amazon S3
- Cloudflare R2
- Google Cloud Storage
- Vercel Blob
- GitHub
- Local filesystem
import { Storage } from "@storagesdk/core";
import { tigris } from "@storagesdk/adapters/tigris";
const storage = new Storage({
adapter: tigris({
bucket: "agent-runs",
accessKeyId: process.env.TIGRIS_ACCESS_KEY_ID,
secretAccessKey: process.env.TIGRIS_SECRET_ACCESS_KEY,
}),
});
await storage.upload("hello.txt", "Hello, storage SDK!", {
contentType: "text/plain",
});
const text = await storage.download("hello.txt", { as: "text" });
The rest of your code doesn't need to see the man behind the curtain. It just stores and reads files.
Portable, but actually this time
Here's all the providers we support out of the gate:
- Amazon S3
- Azure Blob Storage
- Cloudflare R2
- Fly.io
- GitHub
- Google Cloud Storage
- Local Filesystem
- MinIO AIStor
- Railway Buckets
- Tigris
- Vercel Blob Storage
Bed, Bath, and Beyond
We could have stopped here and it would have been pretty great. This general shape of problem and solution really does meet the needs of developers building with object storage. You've got the bed, you've got the bath, but what about the beyond? What if your storage SDK also gave you the ability to time travel? StorageSDK lets you do that.
const snap = await storage.snapshots.create({ name: "baseline" });
await storage.forks.create({
name: "agent-run-123",
fromSnapshot: snap.id,
});
const fork = storage.forks.get("agent-run-123");
await fork.upload("output.json", result);
Branch a bucket per agent run. Mutate safely. Replay from the same baseline. The mapping holds across every adapter:
- snapshot = tag
- fork = branch
- parent bucket = mainline state
- forked bucket = writable isolated workspace
It works on GitHub, Tigris, S3, GCS, and more on the way.
The future looks bright. StorageSDK gives you the same primitives developers already rely on: tags, branches, isolated mutations, and replayable state.