Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.story.foundation/llms.txt

Use this file to discover all available pages before exploring further.

Consumer

The Consumer sub-client handles read requests, partial decryption collection from validators, and final decryption. Requires a walletClient.
const consumer = client.consumer;
The SDK also exposes readVault as an alias for accessCDR, and readFileVault as an alias for downloadFile.
Partial decryptions are collected from the Story-API REST endpoint (/dkg/cdr_partials), keyed by (uuid, requesterPubKey). The keeper verifies each validator’s signature on ingress, so the SDK does not re-verify signatures locally. For defense-in-depth, pass an attestationConfig to verify each validator’s SGX enclave before accepting their partials.

Methods

  • accessCDR
  • downloadFile
  • read
  • collectPartials
  • decryptDataKey
  • prefetchRegistry

accessCDR

High-level method that submits a read request, collects partial decryptions from validators, and combines them to recover the original data.
MethodType
accessCDR(params: AccessCDRParams) => Promise<AccessCDRResponse>
Parameters:
  • params.uuid: number - The vault UUID
  • params.accessAuxData: `0x${string}` - Auxiliary data passed to the read condition
  • params.requesterPubKey (optional): `0x${string}` - Uncompressed secp256k1 public key (65 bytes, 0x04 prefix). If omitted, the SDK generates an ephemeral keypair.
  • params.recipientPrivKey (optional): Uint8Array - 32-byte secp256k1 private key (for ECIES decryption of partials). If omitted, the SDK generates an ephemeral keypair and zeroes it after use.
  • params.globalPubKey (optional): Uint8Array - DKG global public key (from observer.getGlobalPubKey()). If omitted, the SDK queries it for you.
  • params.timeoutMs (optional): number - Timeout for collecting partials. 120_000 is a good starting point.
  • params.feeOverride (optional): bigint - Explicit read fee. Skips the readFee() auto-query (strict-equality semantics — not a way to pay a different amount).
  • params.onInvalidPartial (optional): (event, error) => void - Called when a validator’s partial is excluded because its attestation failed the attestationConfig checks.
  • params.attestationConfig (optional): AttestationConfig - Verifies each validator’s SGX enclave before accepting their partials.
Example
const { dataKey, txHash } = await client.consumer.accessCDR({
  uuid: 42,
  accessAuxData: "0x",
  timeoutMs: 120_000,
});

const secret = new TextDecoder().decode(dataKey);
console.log(`Read tx: ${txHash}`);
console.log(`Decrypted: ${secret}`);
If you want the shortest high-level path, you can omit requesterPubKey, recipientPrivKey, and globalPubKey and let accessCDR() fill them in. The threshold is derived automatically from the partial-decryption bucket’s DKG round.
Throws EmptyVaultError synchronously if the vault has never been written to. This is raised by a preflight chain read before the fee-bearing read() transaction is submitted, so no fee is spent.
AccessCDRResponse
interface AccessCDRResponse {
  dataKey: Uint8Array; // the recovered plaintext
  txHash: `0x${string}`; // read request transaction hash
}

downloadFile

High-level method that reads the encrypted file key through CDR, downloads the encrypted blob from a StorageProvider, and returns the decrypted file bytes. Parameters:
  • params.uuid: number - The vault UUID
  • params.accessAuxData: `0x${string}` - Auxiliary data passed to the read condition
  • params.storageProvider: StorageProvider - Backend used to fetch the encrypted content
  • params.requesterPubKey (optional): `0x${string}` - Explicit requester public key for the read flow
  • params.recipientPrivKey (optional): Uint8Array - Explicit recipient private key for the read flow
  • params.globalPubKey (optional): Uint8Array - DKG global public key. Auto-queried if omitted.
  • params.timeoutMs (optional): number - Timeout for validator partial collection
  • params.feeOverride (optional): bigint - Explicit read fee. Skips the readFee() auto-query.
  • params.onInvalidPartial (optional): (event, error) => void - Called when a validator’s partial is excluded because its attestation failed the attestationConfig checks.
  • params.attestationConfig (optional): AttestationConfig - Verifies each validator’s SGX enclave before accepting their partials.
  • params.skipCidVerification (optional): boolean - Skip CID integrity verification of the downloaded encrypted file (default: false)
Example
import { writeFile } from "node:fs/promises";

const { content } = await client.consumer.downloadFile({
  uuid: 42,
  accessAuxData: "0x",
  storageProvider,
  timeoutMs: 120_000,
});

await writeFile("./example.decrypted.pdf", Buffer.from(content));
console.log("Saved ./example.decrypted.pdf");
For Story license-gated reads, accessAuxData should encode the caller’s license token IDs as abi.encode(uint256[] licenseTokenIds).
downloadFile() inherits the same optional key auto-management behavior as accessCDR(), and returns cid and txHash alongside content.
content is the raw decrypted file bytes. Decode it as text only if the original file was text-based.

read

Submits a read request on-chain. The caller must satisfy the vault’s read condition. This prompts validators to submit encrypted partial decryptions.
MethodType
read(params: ReadParams) => Promise<ReadResponse>
Parameters:
  • params.uuid: number - The vault UUID
  • params.accessAuxData: `0x${string}` - Auxiliary data passed to the read condition
  • params.requesterPubKey: `0x${string}` - Your ephemeral uncompressed secp256k1 public key. Partials are indexed by this value.
  • params.feeOverride (optional): bigint - Explicit read fee. Skips the readFee() auto-query.
Example
const { txHash } = await client.consumer.read({
  uuid: 42,
  accessAuxData: "0x",
  requesterPubKey,
});
ReadResponse
interface ReadResponse {
  txHash: `0x${string}`;
}

collectPartials

Polls the Story-API REST endpoint (/dkg/cdr_partials) until at least a threshold’s worth of partial-decryption submissions have been surfaced for the given (uuid, requesterPubKey).
MethodType
collectPartials(params: CollectPartialsParams) => Promise<PartialDecryptionEvent[]>
Parameters:
  • params.uuid: number - The vault UUID
  • params.requesterPubKey: `0x${string}` - The uncompressed secp256k1 public key used in the matching read() request
  • params.timeoutMs (optional): number - Timeout in milliseconds. 120_000 is a good starting point.
  • params.pollIntervalMs (optional): number - Polling interval in milliseconds
  • params.onInvalidPartial (optional): (event, error) => void - Called when a validator’s partial is excluded because its attestation failed the attestationConfig checks
  • params.attestationConfig (optional): AttestationConfig - Verifies each validator’s SGX enclave and excludes partials from untrusted validators
The required threshold is derived from the partial-decryption bucket’s own DKG round (observer.getThresholdAt(round)), so a DKG rollover mid-poll does not measure the bucket against the wrong round. The vault ciphertext is read once at the start of the call and pinned for the rest of the poll loop.
Throws PartialCollectionTimeoutError if the timeout is reached before enough partials are collected. Throws EmptyVaultError if the vault has never been written to.
Example
const partials = await client.consumer.collectPartials({
  uuid: 42,
  requesterPubKey,
  timeoutMs: 120_000,
});

console.log(`Collected ${partials.length} partials`);
PartialDecryptionEvent
interface PartialDecryptionEvent {
  validator: `0x${string}`;
  round: number;
  pid: number;                      // 1-based participant index
  encryptedPartial: `0x${string}`;  // AES-GCM encrypted
  ephemeralPubKey: `0x${string}`;   // 65 bytes, uncompressed secp256k1
  pubShare: `0x${string}`;          // 34 bytes, Ed25519 with curve-code prefix
  uuid: number;
  ciphertext: `0x${string}`;        // TDH2 ciphertext this partial decrypts
}
Every event returned from a successful collectPartials call shares the same round and ciphertext — the result is filtered to the bucket matching the vault’s current ciphertext.

decryptDataKey

Decrypts the collected partial decryptions using ECIES, then combines them via TDH2 to recover the original plaintext.
MethodType
decryptDataKey(params: DecryptParams) => Promise<Uint8Array>
Parameters:
  • params.ciphertext: TDH2Ciphertext - The encrypted data ({ raw, label })
  • params.partials: PartialDecryptionEvent[] - Collected partial decryptions
  • params.recipientPrivKey: Uint8Array - Your ephemeral secp256k1 private key (32 bytes)
  • params.globalPubKey: Uint8Array - DKG global public key
  • params.label: Uint8Array - 32-byte label (from uuidToLabel(uuid))
The TDH2 combine threshold is taken implicitly as partials.lengthcollectPartials already returns exactly the threshold count needed for reconstruction. Pass exactly the partials you want combined.
Throws InsufficientPartialsError if fewer partials are passed than the ciphertext requires. Throws InvalidCiphertextError if the ciphertext is empty or malformed.
Example
import { uuidToLabel } from "@piplabs/cdr-sdk";

const label = uuidToLabel(uuid);
const dataKey = await client.consumer.decryptDataKey({
  ciphertext: { raw: ciphertextBytes, label },
  partials,
  recipientPrivKey,
  globalPubKey,
  label,
});

const secret = new TextDecoder().decode(dataKey);

prefetchRegistry

Warms the validator commPubKey + attestation cache for the active DKG round. The first accessCDR() / downloadFile() call after construction would otherwise stall on this fetch. Frontends that know a read is imminent (for example, right after wallet connection) can call this in the background.
MethodType
prefetchRegistry() => Promise<void>
Example
// Best-effort warm-up — safe to call repeatedly
client.consumer.prefetchRegistry().catch(() => {});