npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@ckbfs/api

v2.0.19

Published

SDK for CKBFS protocol on CKB

Readme

CKBFS API

A TypeScript SDK for the CKBFS (CKB File System) protocol on the Nervos CKB blockchain. This library provides a high-level interface for publishing, appending, and retrieving files on the decentralized CKB network.

Overview

CKBFS is a file system protocol built on top of the CKB blockchain that enables decentralized file storage with content integrity verification. Files are stored in transaction witnesses with Adler32 checksums for data integrity and support for file versioning through backlinks.

Features

  • File Publishing: Store files of any type on the CKB blockchain
  • File Appending: Add content to existing files with automatic checksum updates
  • Content Integrity: Adler32 checksum verification for all file operations
  • Protocol Versions: Support for V1, V2, and V3 CKBFS protocol versions
  • Witnesses-based Storage: V3 uses cost-efficient witness storage with backlink tracking
  • Network Support: Compatible with CKB mainnet and testnet
  • Chunked Storage: Automatic file chunking for large files

Installation

npm install @ckbfs/api

Quick Start

import { CKBFS, NetworkType, ProtocolVersion } from '@ckbfs/api';

// Initialize with private key
const ckbfs = new CKBFS(
  'your-private-key-here',
  NetworkType.Testnet,
  {
    version: ProtocolVersion.V3, // V3 is now default
    chunkSize: 30 * 1024, // 30KB chunks
    useTypeID: false
  }
);

// Publish a file
const txHash = await ckbfs.publishFile('./example.txt', {
  contentType: 'text/plain',
  filename: 'example.txt'
});

console.log(`File published: ${txHash}`);

API Reference

CKBFS Class

Constructor

new CKBFS(signerOrPrivateKey, networkOrOptions?, options?)

Parameters:

  • signerOrPrivateKey: Signer | string - CKB signer instance or private key
  • networkOrOptions: NetworkType | CKBFSOptions - Network type or configuration options
  • options: CKBFSOptions - Additional configuration when using private key

CKBFSOptions:

interface CKBFSOptions {
  chunkSize?: number;     // Default: 30KB
  version?: string;       // Default: V3
  useTypeID?: boolean;    // Default: false
  network?: NetworkType;  // Default: Testnet
}

Methods

publishFile(filePath, options?)

Publishes a file to CKBFS from the file system.

async publishFile(filePath: string, options?: FileOptions): Promise<string>
publishContent(content, options)

Publishes content directly without reading from file system.

async publishContent(
  content: string | Uint8Array, 
  options: PublishContentOptions
): Promise<string>
appendFile(filePath, ckbfsCell, options?)

Appends content from a file to an existing CKBFS file.

async appendFile(
  filePath: string,
  ckbfsCell: AppendOptions['ckbfsCell'],
  options?: Omit<FileOptions, 'contentType' | 'filename'>
): Promise<string>
appendContent(content, ckbfsCell, options?)

Appends content directly to an existing CKBFS file.

async appendContent(
  content: string | Uint8Array,
  ckbfsCell: AppendOptions['ckbfsCell'],
  options?: AppendContentOptions
): Promise<string>
Transaction Creation Methods

Create unsigned transactions for custom signing workflows:

  • createPublishTransaction(filePath, options?)
  • createPublishContentTransaction(content, options)
  • createAppendTransaction(filePath, ckbfsCell, options?)
  • createAppendContentTransaction(content, ckbfsCell, options?)

Types

FileOptions

interface FileOptions {
  contentType?: string;
  filename?: string;
  capacity?: bigint;
  feeRate?: number;
  network?: NetworkType;
  version?: string;
  useTypeID?: boolean;
}

PublishContentOptions

type PublishContentOptions = Omit<FileOptions, 'capacity' | 'contentType' | 'filename'> & 
  Required<Pick<FileOptions, 'contentType' | 'filename'>> & 
  { capacity?: bigint };

NetworkType

enum NetworkType {
  Mainnet = 'mainnet',
  Testnet = 'testnet'
}

Protocol Versions

const ProtocolVersion = {
  V1: '20240906.ce6724722cf6',  // Original version
  V2: '20241025.db973a8e8032'   // Enhanced version with multi-witness support
} as const;

Examples

Publishing a File

import { CKBFS, NetworkType, ProtocolVersion } from '@ckbfs/api';

const ckbfs = new CKBFS('your-private-key', NetworkType.Testnet);

// Publish with automatic content type detection
const txHash = await ckbfs.publishFile('./document.pdf');

// Publish with custom options
const txHash2 = await ckbfs.publishFile('./data.json', {
  contentType: 'application/json',
  filename: 'my-data.json',
  capacity: 300n * 100000000n // 300 CKB
});

Publishing Content Directly

const content = "Hello, CKBFS!";
const txHash = await ckbfs.publishContent(content, {
  contentType: 'text/plain',
  filename: 'greeting.txt'
});

Appending to a File

// First, get the CKBFS cell information from a previous transaction
const ckbfsCell = await getCellInfoFromTransaction(previousTxHash);

// Append content from a file
const appendTxHash = await ckbfs.appendFile('./additional-content.txt', ckbfsCell);

// Or append content directly
const appendTxHash2 = await ckbfs.appendContent(
  "Additional content to append",
  ckbfsCell
);

Retrieving Files

Traditional Method (with blockchain queries)

import { getFileContentFromChain, saveFileFromChain } from '@ckbfs/api';
import { ClientPublicTestnet } from '@ckb-ccc/core';

const client = new ClientPublicTestnet();

// Get file content from blockchain (follows backlinks automatically)
const content = await getFileContentFromChain(
  client,
  { txHash: 'transaction-hash', index: 0 },
  ckbfsData
);

// Save to disk
const savedPath = saveFileFromChain(content, ckbfsData, './downloaded-file.txt');

Generic Identifier Retrieval (flexible interface)

import { 
  getFileContentFromChainByIdentifier,
  saveFileFromChainByIdentifier,
  decodeFileFromChainByIdentifier,
  parseIdentifier,
  IdentifierType
} from '@ckbfs/api';
import { ClientPublicTestnet } from '@ckb-ccc/core';

const client = new ClientPublicTestnet();

// Supported identifier formats:
const typeIdHex = '0xbce89252cece632ef819943bed9cd0e2576f8ce26f9f02075b621b1c9a28056a';
const ckbfsTypeIdUri = 'ckbfs://bce89252cece632ef819943bed9cd0e2576f8ce26f9f02075b621b1c9a28056a';
const ckbfsOutPointUri = 'ckbfs://431c9d668c1815d26eb4f7ac6256eb350ab351474daea8d588400146ab228780i0';

// Parse identifier to see what type it is
const parsed = parseIdentifier(ckbfsTypeIdUri);
console.log(`Type: ${parsed.type}`); // "typeId" or "outPoint"

// Get file content using any identifier format (follows backlinks automatically)
const fileData = await getFileContentFromChainByIdentifier(client, ckbfsTypeIdUri, {
  network: 'testnet',
  version: ProtocolVersion.V3, // V3 supports witness-based backlinks
  useTypeID: false
});

if (fileData) {
  console.log(`File: ${fileData.filename}`);
  console.log(`Size: ${fileData.size} bytes`);
  console.log(`Content type: ${fileData.contentType}`);
  console.log(`Parsed as: ${fileData.parsedId.type}`);
}

// Save file using outPoint format
const savedPath = await saveFileFromChainByIdentifier(
  client, 
  ckbfsOutPointUri, 
  './downloaded-file.txt'
);

// Decode using direct witness method with TypeID hex
const decodedData = await decodeFileFromChainByIdentifier(client, typeIdHex);

TypeID-based Retrieval (legacy interface)

import { 
  getFileContentFromChainByTypeId,
  saveFileFromChainByTypeId,
  decodeFileFromChainByTypeId 
} from '@ckbfs/api';

// Legacy functions still work with TypeID strings
const fileData = await getFileContentFromChainByTypeId(client, typeId);
const savedPath = await saveFileFromChainByTypeId(client, typeId, './file.txt');
const decodedData = await decodeFileFromChainByTypeId(client, typeId);

Direct Witness Decoding (new method)

import { 
  decodeWitnessContent,
  decodeFileFromWitnessData,
  saveFileFromWitnessData 
} from '@ckbfs/api';

// Decode individual witness
const decoded = decodeWitnessContent(witnessHex);
if (decoded && decoded.isValid) {
  console.log(`Content: ${decoded.content.length} bytes`);
}

// Decode complete file from witness data
const file = decodeFileFromWitnessData({
  witnesses: tx.witnesses,
  indexes: [1, 2, 3], // witness indexes containing content
  filename: 'example.txt',
  contentType: 'text/plain'
});

// Save directly from witness data
const savedPath = saveFileFromWitnessData({
  witnesses: tx.witnesses,
  indexes: [1, 2, 3],
  filename: 'example.txt',
  contentType: 'text/plain'
}, './decoded-file.txt');

Utility Functions

The SDK exports various utility functions for advanced use cases:

Checksum Operations

import { calculateChecksum, verifyChecksum, updateChecksum } from '@ckbfs/api';

const checksum = await calculateChecksum(fileData);
const isValid = await verifyChecksum(fileData, expectedChecksum);
const newChecksum = await updateChecksum(oldChecksum, appendedData);

File Operations

import { 
  readFileAsUint8Array, 
  getContentType, 
  splitFileIntoChunks,
  getFileContentFromChainByIdentifier,
  saveFileFromChainByIdentifier,
  decodeFileFromChainByIdentifier,
  parseIdentifier,
  IdentifierType
} from '@ckbfs/api';

const fileData = readFileAsUint8Array('./file.txt');
const mimeType = getContentType('./file.txt');
const chunks = splitFileIntoChunks('./large-file.bin', 30 * 1024);

// Generic identifier-based file operations
const client = new ClientPublicTestnet();
const identifier = 'ckbfs://bce89252cece632ef819943bed9cd0e2576f8ce26f9f02075b621b1c9a28056a';
const parsed = parseIdentifier(identifier);
const fileContent = await getFileContentFromChainByIdentifier(client, identifier);
const savedPath = await saveFileFromChainByIdentifier(client, identifier, './output.txt');
const decodedFile = await decodeFileFromChainByIdentifier(client, identifier);

Witness Operations

import { 
  createCKBFSWitness, 
  extractCKBFSWitnessContent, 
  isCKBFSWitness 
} from '@ckbfs/api';

const witness = createCKBFSWitness(contentBytes);
const { version, content } = extractCKBFSWitnessContent(witness);
const isValid = isCKBFSWitness(witness);

Identifier Formats

CKBFS supports multiple identifier formats for flexible file access:

1. TypeID Hex String

0xbce89252cece632ef819943bed9cd0e2576f8ce26f9f02075b621b1c9a28056a

Direct TypeID from the CKBFS cell's type script args.

2. CKBFS TypeID URI

ckbfs://bce89252cece632ef819943bed9cd0e2576f8ce26f9f02075b621b1c9a28056a

CKBFS URI format using TypeID (without 0x prefix).

3. CKBFS OutPoint URI

ckbfs://431c9d668c1815d26eb4f7ac6256eb350ab351474daea8d588400146ab228780i0

CKBFS URI format using transaction hash and output index: ckbfs://{txHash}i{index}

Identifier Detection

import { parseIdentifier, IdentifierType } from '@ckbfs/api';

const parsed = parseIdentifier('ckbfs://abc123...i0');
console.log(parsed.type); // IdentifierType.OutPoint
console.log(parsed.txHash); // '0xabc123...'
console.log(parsed.index); // 0

Protocol Details

CKBFS Data Structure

CKBFS uses a molecule-encoded data structure stored in cell output data:

V1 Format:

{
  index: number,           // Single witness index
  checksum: number,        // Adler32 checksum
  contentType: string,     // MIME type
  filename: string,        // Original filename
  backLinks: BackLink[]    // Previous versions
}

V2 Format:

{
  indexes: number[],       // Multiple witness indexes
  checksum: number,        // Adler32 checksum
  contentType: string,     // MIME type
  filename: string,        // Original filename
  backLinks: BackLink[]    // Previous versions
}

V3 Format:

{
  index: number,           // Single witness index (first content witness)
  checksum: number,        // Adler32 checksum
  contentType: string,     // MIME type
  filename: string,        // Original filename
  // Note: No backLinks in cell data - moved to witnesses for cost efficiency
}

Witness Format

V1/V2 Witnesses:

  • 5-byte header: "CKBFS" (0x43, 0x4B, 0x42, 0x46, 0x53)
  • 1-byte version: 0x00 or 0x02
  • Variable-length content data

V3 Witnesses (Enhanced with Backlinks):

  • 5-byte header: "CKBFS" (0x43, 0x4B, 0x42, 0x46, 0x53)
  • 1-byte version: 0x03
  • 32-byte previous transaction hash
  • 4-byte previous witness index
  • 4-byte previous checksum
  • 4-byte next witness index (or 0x00000000 for tail)
  • Variable-length content data

Checksum Algorithm

CKBFS uses Adler32 for content integrity verification. When appending content, the checksum is updated using the rolling Adler32 algorithm to maintain cumulative integrity across all file versions.

Development

Building

npm run build

Testing

npm test

Running Examples

# Publish example
npm run example:publish

# Append example (requires existing transaction hash)
npm run example:append -- --txhash=0x123456...

# Retrieve example (demonstrates witness decoding and generic identifier APIs)
npm run example:retrieve -- --txhash=0x123456...

# V3 specific examples
npm run example:publish-v3
npm run example:append-v3 -- --txhash=0x123456...

# All examples
npm run example

Environment Variables

  • CKB_PRIVATE_KEY: Your CKB private key for examples
  • PUBLISH_TX_HASH: Transaction hash for append examples
  • TARGET_TX_HASH: Transaction hash for retrieve examples

Advanced Usage

Working with Different Identifier Formats

import { 
  getFileContentFromChainByIdentifier,
  parseIdentifier,
  IdentifierType 
} from '@ckbfs/api';

// Example identifiers
const identifiers = [
  '0xabc123...', // TypeID hex
  'ckbfs://abc123...', // CKBFS TypeID URI
  'ckbfs://def456...i0' // CKBFS OutPoint URI
];

// Process any identifier format
for (const id of identifiers) {
  const parsed = parseIdentifier(id);
  console.log(`Processing ${parsed.type} identifier`);
  
  const fileData = await getFileContentFromChainByIdentifier(client, id);
  if (fileData) {
    console.log(`Retrieved: ${fileData.filename}`);
  }
}

Batch File Operations

// Retrieve multiple files using different identifier formats
const fileIdentifiers = [
  'ckbfs://file1-typeid...',
  'ckbfs://tx-hash1...i0',
  '0xfile2-typeid...'
];

const files = await Promise.all(
  fileIdentifiers.map(id => 
    getFileContentFromChainByIdentifier(client, id)
  )
);

files.forEach((file, index) => {
  if (file) {
    console.log(`File ${index + 1}: ${file.filename} (${file.size} bytes)`);
  }
});

Network Configuration

The SDK supports both CKB mainnet and testnet with different deployed contract addresses:

Testnet (Default)

  • CKBFS V1: 0xe8905ad29a02cf8befa9c258f4f941773839a618d75a64afc22059de9413f712
  • CKBFS V2: 0x31e6376287d223b8c0410d562fb422f04d1d617b2947596a14c3d2efb7218d3a

Mainnet

  • CKBFS V2: 0x31e6376287d223b8c0410d562fb422f04d1d617b2947596a14c3d2efb7218d3a

License

MIT

Contributing

Contributions are welcome. Please ensure all tests pass and follow the existing code style.

Support

For issues and questions, please use the GitHub issue tracker.