Skip to content

Manage Files and Buckets

This guide explains how to manage your storage resources on DataHaven using the StorageHub SDK. You will learn how to request the removal of a file from the network and how to delete buckets. It's important to periodically review and clean up unused data to avoid unnecessary costs, as buckets and files incur ongoing storage fees.

Prerequisites

  • Node.js v22+ installed
  • A TypeScript project

    Need a starter project?

    If you don't have an existing project, follow these steps to create a TypeScript project you can use to follow the guides in this section:

    1. Create a new project folder by executing the following command in the terminal:

      mkdir datahaven-project && cd datahaven-project
      
    2. Initialize a package.json file using the correct command for your package manager:

      pnpm init
      
      yarn init
      
      npm init --y
      
    3. Add the TypeScript and Node type definitions to your projects using the correct command for your package manager:

      pnpm add -D typescript ts-node @types/node
      
      yarn add -D typescript ts-node @types/node
      
      npm install -D typescript ts-node @types/node
      
    4. Create a tsconfig.json file in the root of your project and paste the following configuration:

      tsconfig.json
      {
          "compilerOptions": {
              "target": "ES2022",
              "module": "nodenext",
              "moduleResolution": "NodeNext",
              "esModuleInterop": true,
              "strict": true,
              "skipLibCheck": true,
              "outDir": "dist",
              "declaration": true,
              "sourceMap": true
          },
          "include": ["src/**/*.ts"]
      }
      
    5. Initialize the src directory:

      mkdir src && touch src/index.ts
      
  • Dependencies installed

  • Clients initialized

  • A file uploaded to DataHaven, along with the file key

Initialize the Script Entry Point

First, create an index.ts file if you haven't already. Its run method will orchestrate all the logic in this guide, and you’ll replace the labelled placeholders with real code step by step. By now, your services folder (including the MSP and client helper services) should already be created. If not, see the Get Started guide.

The index.ts snippet below also imports bucketOperations.ts and fileOperations.ts, which are not in your project yet—that's expected, as you'll create them later in this guide.

Add the following code to your index.ts file:

index.ts
import '@storagehub/api-augment';
import { initWasm } from '@storagehub-sdk/core';
import { polkadotApi } from './services/clientService.js';
import { authenticateUser } from './services/mspService.js';
import { requestDeleteFile } from './operations/fileOperations.js';
import { deleteBucket } from './operations/bucketOperations.js';

async function run() {
  const bucketId = 'INSERT_BUCKET_ID'; // `0x${string}`
  const fileKey = 'INSERT_FILE_KEY'; // `0x${string}`
  // If not in hex already, convert it with .toHex()

  // --- Data deletion logic ---
  // **PLACEHOLDER FOR STEP 1: AUTHENTICATE**
  // **PLACEHOLDER FOR STEP 2: REQUEST FILE DELETION**
  // **PLACEHOLDER FOR STEP 3: DELETE A BUCKET**

  // Disconnect the Polkadot API at the very end
  await polkadotApi.disconnect();
}

await run();

Authenticate

Before any file operations, authenticate with the MSP. The authenticateUser helper signs a SIWE message and returns a session token that authorizes your uploads, updates, and deletions. Add the following code to use the authenticateUser helper method you've already implemented in mspService.ts:

index.ts // **PLACEHOLDER FOR STEP 1: AUTHENTICATE**
// Authenticate
const authProfile = await authenticateUser();
console.log('Authenticated user profile:', authProfile);
View complete index.ts up until this point
index.ts
  import '@storagehub/api-augment';
  import { initWasm } from '@storagehub-sdk/core';
  import { polkadotApi } from './services/clientService.js';
  import { authenticateUser } from './services/mspService.js';
  import { requestDeleteFile } from './operations/fileOperations.js';
  import { deleteBucket } from './operations/bucketOperations.js';

  async function run() {
  const bucketId = 'INSERT_BUCKET_ID'; // `0x${string}`
  const fileKey = 'INSERT_FILE_KEY'; // `0x${string}`
  // If not in hex already, convert it with .toHex()

  // --- Data deletion logic ---
  // Authenticate
  const authProfile = await authenticateUser();
  console.log('Authenticated user profile:', authProfile);
  // **PLACEHOLDER FOR STEP 2: REQUEST FILE DELETION**
  // **PLACEHOLDER FOR STEP 3: DELETE A BUCKET**

  // Disconnect the Polkadot API at the very end
  await polkadotApi.disconnect();
}

await run();

Request File Deletion

To request file deletion, create a helper method called requestDeleteFile in a separate fileOperations.ts file and then update the index.ts file accordingly, in order to execute that logic. First, fetch the file’s metadata from the MSP and then submit a deletion request using the StorageHub SDK.

It’s important to note that files are not removed instantly. When a deletion request succeeds, the file is marked for deletion on-chain, but both the MSP and all BSPs storing that file still have the file inside their Merkle Patricia Forests until they pass the mandatory storage proof challenge. After that, the runtime automatically updates their Merkle Patricia Forest roots to remove the file.

Add Method to Request File Deletion

  1. Create a new folder called operations within the src folder (at the same level as the services folder) like so:

    mkdir operations
    
  2. Create a new file within the operations folder called fileOperations.ts.

  3. Add the following code:

    fileOperations.ts
    import { storageHubClient, publicClient } from '../services/clientService.js';
    import { mspClient } from '../services/mspService.js';
    import { FileInfo } from '@storagehub-sdk/core';
    
    export async function requestDeleteFile(
      bucketId: string,
      fileKey: string
    ): Promise<boolean> {
      // Get file info before deletion
      const fileInfo: FileInfo = await mspClient.files.getFileInfo(
        bucketId,
        fileKey
      );
      console.log('File info:', fileInfo);
    
      // Request file deletion
      const txHashRequestDeleteFile: `0x${string}` =
        await storageHubClient.requestDeleteFile(fileInfo);
      console.log('requestDeleteFile() txHash:', txHashRequestDeleteFile);
    
      // Wait for delete file transaction receipt
      const receiptRequestDeleteFile = await publicClient.waitForTransactionReceipt(
        {
          hash: txHashRequestDeleteFile,
        }
      );
      console.log('requestDeleteFile() txReceipt:', receiptRequestDeleteFile);
      if (receiptRequestDeleteFile.status !== 'success') {
        throw new Error(`File deletion failed: ${txHashRequestDeleteFile}`);
      }
    
      console.log(
        `File with key ${fileKey} deleted successfully from bucket ${bucketId}`
      );
      return true;
    }
    

Call the Request File Deletion Helper Method

Update index.ts with the following code to trigger the requestDeleteFile helper method you just implemented:

index.ts // **PLACEHOLDER FOR STEP 2: REQUEST FILE DELETION**
// Request file deletion
const isDeletionRequestSuccessful = await requestDeleteFile(
  bucketId,
  fileKey
);
console.log(
  'File deletion request submitted successfully:',
  isDeletionRequestSuccessful
);
View complete index.ts up until this point
index.ts
import '@storagehub/api-augment';
import { initWasm } from '@storagehub-sdk/core';
import { polkadotApi } from './services/clientService.js';
import { authenticateUser } from './services/mspService.js';
import { requestDeleteFile } from './operations/fileOperations.js';
import { deleteBucket } from './operations/bucketOperations.js';

async function run() {
  const bucketId = 'INSERT_BUCKET_ID'; // `0x${string}`
  const fileKey = 'INSERT_FILE_KEY'; // `0x${string}`
  // If not in hex already, convert it with .toHex()

  // --- Data deletion logic ---
  // Authenticate
  const authProfile = await authenticateUser();
  console.log('Authenticated user profile:', authProfile);
  // Request file deletion
  const isDeletionRequestSuccessful = await requestDeleteFile(
    bucketId,
    fileKey
  );
  console.log(
    'File deletion request submitted successfully:',
    isDeletionRequestSuccessful
  );
  // **PLACEHOLDER FOR STEP 3: DELETE A BUCKET**

  // Disconnect the Polkadot API at the very end
  await polkadotApi.disconnect();
}

await run();

Note

After a file is uploaded to a Main Storage Provider (MSP), the network allows a 10-minute window for Backup Storage Providers (BSPs) to replicate the file to the required count. Within this time window, the deletion of a file cannot be requested. If the replication target is not met within this window, the request transitions to expired even though the upload to the MSP succeeded.

If you run the script with the code above, the full response should look like this:

ts-node index.ts
File info: {
 fileKey: '0xe0d46d3e57ab2aabd09aee9cf8425910de5028bc82a1a8c52774b7c71d6c8933',
  fingerprint: '0x1bc3a71173c16c1eee04f7e7cf2591678b0b6cdf08eb81c638ae60a38b706aad',
  bucketId: '0x5811b0cfc1529286c59988e55e3e7d701ba2d68bdebdafb9b891f0c887611108',
  location: 'helloworld.txt',
  size: 18,
  isPublic: true,
  uploadedAt: 2025-11-05T14:19:55.911Z,
  status: 'ready'
}
requestDeleteFile() txHash: 0x5e33858fb2399ffbfaa90f9abb1bb6d06f978c5b25fe9d552a05ac6fd17f055c
requestDeleteFile() txReceipt: {
  transactionHash: '0x5e33858fb2399ffbfaa90f9abb1bb6d06f978c5b25fe9d552a05ac6fd17f055c',
  transactionIndex: 0,
  blockHash: '0xd3ad1ede25a2abb1673c26cac35c55ca3aaf6976db181582525838ff4b5b9498',
  from: '0x00fa35d84a43db75467d2b2c1ed8974aca57223e',
  to: '0x0000000000000000000000000000000000000404',
  blockNumber: 115472n,
  cumulativeGasUsed: 32400n,
  gasUsed: 32400n,
  contractAddress: null,
  logs: [
    {
      address: '0x0000000000000000000000000000000000000404',
      topics: [Array],
      data: '0x',
      blockHash: '0xd3ad1ede25a2abb1673c26cac35c55ca3aaf6976db181582525838ff4b5b9498',
      blockNumber: 115472n,
      transactionHash: '0x5e33858fb2399ffbfaa90f9abb1bb6d06f978c5b25fe9d552a05ac6fd17f055c',
      transactionIndex: 0,
      logIndex: 0,
      transactionLogIndex: '0x0',
      removed: false
    }
  ],
  logsBloom: '0x00000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000004000000000200000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000800000004000000080000000000000000000000000000000000000010000000000000000000000000000080000',
  status: 'success',
  effectiveGasPrice: 1000000000n,
  type: 'legacy'
}
File with key 0xe0d46d3e57ab2aabd09aee9cf8425910de5028bc82a1a8c52774b7c71d6c8933 deleted successfully from bucket 0x5811b0cfc1529286c59988e55e3e7d701ba2d68bdebdafb9b891f0c887611108 File deletion request submitted successfully: true

Delete a Bucket

To delete a bucket, create a helper method called deleteBucket in a separate bucketOperations.ts file and then update the index.ts file accordingly, in order to execute that logic.

Note

A bucket can only be deleted if all its files have already been deleted. Use the mspClient.buckets.getFiles() method by passing a bucketId as a parameter to check all the files currently stored in that bucket.

Add Method to Delete Bucket

Create a new file within the operations folder called bucketOperations.ts and add the following code:

bucketOperations.ts
import { storageHubClient, publicClient } from '../services/clientService.js';

export async function deleteBucket(bucketId: string): Promise<boolean> {
  const txHash: `0x${string}` | undefined = await storageHubClient.deleteBucket(
    bucketId as `0x${string}`
  );
  console.log('deleteBucket() txHash:', txHash);
  if (!txHash) {
    throw new Error('deleteBucket() did not return a transaction hash');
  }

  // Wait for transaction
  const receipt = await publicClient.waitForTransactionReceipt({
    hash: txHash,
  });
  console.log('deleteBucket() txReceipt:', receipt);
  if (receipt.status !== 'success') {
    throw new Error(`Bucket deletion failed: ${txHash}`);
  }

  return true;
}

Call the Delete Bucket Helper Method

Update index.ts with the following code to trigger the deleteBucket helper method you just implemented:

index.ts
const IsBucketDeletionSuccessful = await deleteBucket(bucketId);
console.log('Bucket deletion successful:', IsBucketDeletionSuccessful);
View complete index.ts
index.ts
import '@storagehub/api-augment';
import { initWasm } from '@storagehub-sdk/core';
import { polkadotApi } from './services/clientService.js';
import { authenticateUser } from './services/mspService.js';
import { requestDeleteFile } from './operations/fileOperations.js';
import { deleteBucket } from './operations/bucketOperations.js';

async function run() {
  // Initialize WASM
  await initWasm();

  const bucketId = 'INSERT_BUCKET_ID'; // `0x${string}`
  const fileKey = 'INSERT_FILE_KEY'; // `0x${string}`
  // If not in hex already, convert it with .toHex()

  // Authenticate
  const authProfile = await authenticateUser();
  console.log('Authenticated user profile:', authProfile);

  // Request file deletion
  const isDeletionRequestSuccessful = await requestDeleteFile(
    bucketId,
    fileKey
  );
  console.log(
    'File deletion request submitted successfully:',
    isDeletionRequestSuccessful
  );

  const IsBucketDeletionSuccessful = await deleteBucket(bucketId);
  console.log('Bucket deletion successful:', IsBucketDeletionSuccessful);

  await polkadotApi.disconnect();
}

run();

If you run the script with the bucket deletion code, the response should include:

ts-node index.ts
deleteBucket() txHash: 0x30204c4e27430ad81067e3dcbb283e46dac3a8a2ca484c980277056658128f13
deleteBucket() txReceipt: {
  transactionHash: '0x30204c4e27430ad81067e3dcbb283e46dac3a8a2ca484c980277056658128f13',
  transactionIndex: 0,
  blockHash: '0x2784726df3ea7f5333749c54f9c62e1813404a5475e0d040a456194c2636d2d2',
  from: '0x00fa35d84a43db75467d2b2c1ed8974aca57223e',
  to: '0x0000000000000000000000000000000000000404',
  blockNumber: 117400n,
  cumulativeGasUsed: 228432n,
  gasUsed: 228432n,
  contractAddress: null,
  logs: [
    {
      address: '0x0000000000000000000000000000000000000404',
      topics: [Array],
      data: '0x',
      blockHash: '0x2784726df3ea7f5333749c54f9c62e1813404a5475e0d040a456194c2636d2d2',
      blockNumber: 117400n,
      transactionHash: '0x30204c4e27430ad81067e3dcbb283e46dac3a8a2ca484c980277056658128f13',
      transactionIndex: 0,
      logIndex: 0,
      transactionLogIndex: '0x0',
      removed: false
    }
  ],
  logsBloom: '0x00000000000000040000000000000000000000000000000000000000000000000000000000000000000000000008000008000000000010000000000000000000000000040000000000020000000000000000000000000100000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000010000800000000000000000000000080000',
  status: 'success',
  effectiveGasPrice: 1000000000n,
  type: 'legacy'
}
Bucket deletion successful: true

Next Steps

Last update: December 18, 2025
| Created: November 7, 2025