Get started
storagesdk is a multi-provider TypeScript SDK for object storage — with fork and
snapshot as primitives. Install the package, pick an adapter, pass it to Storage,
and start calling it.
Overview
Every operation is the same shape on every provider. You build a Storage by passing it
an adapter — that’s it. The adapter handles auth, request signing, and any quirks of
the underlying service.
Snapshots and forks are first-class. On Tigris they map to the native APIs; on every other provider the SDK emulates them as sibling buckets and bookkeeps the metadata so you don’t have to. The call site stays the same either way.
- One API, every provider — Tigris, Amazon S3, Cloudflare R2, GCS, Azure Blob, Vercel Blob, MinIO, and the local filesystem.
- Snapshot and fork as primitives — branch a bucket per agent run, freeze a moment in time, replay it deterministically.
- Web-standard streams in and out,
AbortSignalon every method, typedStorageErrorcodes. - Typed escape hatch via
storage.rawwhen you need an adapter-specific operation.
Installation
Install the core package and the adapters bundle with your package manager of choice:
npm install @storagesdk/core @storagesdk/adaptersUsage
Construct a Storage, then call its methods. upload() takes a string, Uint8Array,
Blob, or a web ReadableStream. download() is overloaded by destination type — pass
as: 'text', 'bytes', 'stream', or omit it to get the full StorageItem.
import { Storage } from '@storagesdk/core';
import { fs } from '@storagesdk/adapters/fs';
const storage = new Storage({
adapter: fs({ root: './.storage', folder: 'agent-runs' }),
});
await storage.upload('hello.txt', 'Hello, storage SDK!');
const text = await storage.download('hello.txt', { as: 'text' });Snapshot and fork
Freeze a bucket’s state, then branch from that frozen state into a writable fork. Per-agent sandboxes; reproducible runs.
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,
}),
});
// Freeze the current state.
const snap = await storage.snapshots.create({ name: 'pre-migration' });
// Branch from that snapshot — writable, isolated.
await storage.forks.create({ name: 'agent-runs-exp', fromSnapshot: snap.id });
const fork = storage.forks.get('agent-runs-exp');
await fork.upload('hello.txt', 'mutated in fork only');Adapter
Switching providers means changing the import. The rest of your code doesn’t move. Below is the same upload-and-download sequence across every first-party adapter — flip the tab to compare.
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' });Each adapter accepts its provider’s standard config — bucket and credentials for cloud
providers; a local root directory for filesystem. See the Adapters
section for full per-provider configuration, auth, and gotchas.