| name | product-sdk-cloud-storage |
| description | Use when uploading or retrieving data via Cloud Storage, working with CID-based decentralized storage, or the CloudStorageClient SDK. Covers the store() upload builder, chunked uploads, container-only reads (fetch/query), CID helpers, and authorization.
|
Product SDK Cloud Storage
@parity/product-sdk-cloud-storage is a TypeScript SDK for uploading and retrieving data via Cloud Storage -- a purpose-built parachain for decentralized data storage. Data is content-addressed using CIDv1 (blake2b-256 hash, raw codec). It wraps @parity/bulletin-sdk (chunking, DAG-PB manifests, CID calculation, progress events) and routes reads through the host's preimage subscription.
Key Concepts
- Content-addressed storage: Data is identified by its CID (Content Identifier), computed deterministically from the bytes via blake2b-256.
- Uploads always need a signer: Every
store(...) submits a TransactionStorage.store extrinsic, so create(...) requires a signer. In-container vs standalone only changes where the signer comes from (the host's account vs a PolkadotSigner you supply) -- there is no signer-less host upload path.
- Reads are container-only:
fetchBytes/fetchJson/queryBytes resolve content through the host's preimage subscription and require a host container. Outside a container they return err(CloudStorageHostUnavailableError) -- the SDK does not fall back to a public IPFS gateway. A product that needs a standalone read path must branch on isInsideContainer() and perform its own gateway fetch.
- Reads return
Result, not thrown errors: queryBytes/queryJson/executeQuery/checkAuthorization/verifyStored and their CloudStorageClient method equivalents (fetchBytes/fetchJson/checkAuthorization/verifyStored), plus the free authorizeAccount, return Result<T, E> = { ok: true; value: T } | { ok: false; error: E }. Check res.ok and read res.value (or res.error) -- they do not throw. (Upstream .send() builder results and the sync CID helpers are unchanged.)
- Environments:
create({ environment }) accepts "paseo" (Paseo Next v2) and "devnet" (public Paseo testnet) -- the CloudStorageEnvironment presets, keys of CloudStorageNetworks; both currently available. "polkadot"/"kusama" are not yet available (compile error, and getChainAPI throws).
Quick Start: Upload and Fetch
import { CloudStorageClient, createLazySigner } from "@parity/product-sdk-cloud-storage";
const cloudStorage = await CloudStorageClient.create({
environment: "paseo",
signer: createLazySigner(() => getCurrentSigner()),
});
const data = new TextEncoder().encode(JSON.stringify({ title: "Hello Cloud Storage" }));
const result = await cloudStorage.store(data).send();
console.log("CID:", result.cid?.toString());
console.log("block:", result.blockNumber, "size:", result.size);
const content = await cloudStorage.fetchJson<{ title: string }>(result.cid!.toString());
if (!content.ok) throw content.error;
console.log(content.value.title);
WARNING: store() expects Uint8Array, not strings. Always convert with new TextEncoder().encode(...).
CloudStorageClient
The CloudStorageClient wraps the upstream @parity/bulletin-sdk client and adds network presets, container-routed read helpers, and a pre-flight authorization check.
Creating a Client
import { CloudStorageClient, CloudStorageNetworks, createLazySigner } from "@parity/product-sdk-cloud-storage";
const client = await CloudStorageClient.create({ environment: "paseo", signer });
const custom = await CloudStorageClient.create({
...CloudStorageNetworks.paseo,
signer,
config: { defaultChunkSize: 1 << 20 },
});
const fromInner = CloudStorageClient.from(inner, api);
When to use each entry point
| Method | When to use |
|---|
create({ environment, signer }) | Quick start against a preset network |
create({ ...descriptor, signer }) | Custom / BYOD network (explicit genesisHash + descriptor) |
from(inner, api) | You already own the AsyncBulletinClient and its connection lifecycle |
Uploading Data
Uploads use the fluent store(data) builder (re-exported from @parity/bulletin-sdk):
const data = new TextEncoder().encode("raw file content");
const result = await client.store(data).send();
const result2 = await client
.store(data)
.withWaitFor("finalized")
.send();
await client.authorizePreimage(contentHash, BigInt(data.length));
const result3 = await client.store(data).sendUnsigned();
Chunked Uploads (large files)
There is no batchUpload. For large files, force the chunked path with withChunkSize -- a DAG-PB manifest (default) ties the chunks together and result.cid is the manifest CID:
const result = await client
.store(largeFile)
.withChunkSize(1 << 20)
.withCallback((evt) => console.log(evt))
.send();
Fetching Data
fetchBytes/fetchJson are instance methods and container-only (host preimage lookup; return err(CloudStorageHostUnavailableError) standalone). Both return a Result -- check .ok before reading .value:
const res = await client.fetchBytes(cid);
if (!res.ok) return handle(res.error);
const bytes = res.value;
const meta = await client.fetchJson<{ name: string }>(cid);
if (!meta.ok) return handle(meta.error);
const metadata = meta.value;
Utility Methods
import { calculateCid } from "@parity/product-sdk-cloud-storage";
const cid = await calculateCid(data);
const verified = await client.verifyStored(result.cid!.toString(), { block: result.blockNumber! });
if (!verified.ok) throw verified.error;
const entry = verified.value;
const authResult = await client.checkAuthorization(address);
if (!authResult.ok) throw authResult.error;
const auth = authResult.value;
const need = client.estimateAuthorization(data.length);
Standalone Functions
For advanced use cases:
import {
cidToPreimageKey,
hashToCid,
calculateCid,
queryBytes,
queryJson,
executeQuery,
resolveQueryStrategy,
} from "@parity/product-sdk-cloud-storage";
upload, batchUpload, cidExists, gatewayUrl, getGateway, and static computeCid/hashToCid no
longer exist. Uploads go through the store(...) builder; CID computation is the standalone async
calculateCid; fetchBytes/fetchJson are CloudStorageClient instance methods (container-only, no
gateway argument).
Common Mistakes
- Passing a string to
store() instead of Uint8Array - convert with new TextEncoder().encode(...).
- Forgetting that
create() requires a signer - every store() submits a transaction; use createLazySigner if no account is selected yet.
- Treating
result.cid as a string - it's a CID object (or undefined for chunked-without-manifest); call .toString().
- Expecting reads to work standalone -
fetchBytes/fetchJson/queryBytes are container-only and return err(CloudStorageHostUnavailableError) outside a host.
- Using a read result as a plain value -
fetchBytes/fetchJson/queryBytes/queryJson/checkAuthorization/verifyStored (and authorizeAccount) return Result<T, E>. Check res.ok and read res.value -- they do not throw.
Reference Files