@siafoundation/sia
v0.3.0
Published
TypeScript SDK for building decentralized storage apps on the [Sia](https://sia.tech) network.
Readme
@siafoundation/sia
TypeScript SDK for building decentralized storage apps on the Sia network.
Install
npm install @siafoundation/siaBundler setup
This package includes a WebAssembly binary, so your bundler needs to be configured to handle .wasm files.
Vite
npm install vite-plugin-wasm vite-plugin-top-level-await// vite.config.ts
import wasm from 'vite-plugin-wasm'
import topLevelAwait from 'vite-plugin-top-level-await'
import { defineConfig } from 'vite'
export default defineConfig({
plugins: [wasm(), topLevelAwait()],
optimizeDeps: {
exclude: ['@siafoundation/sia'],
},
resolve: {
alias: {
'sia-wasm': '@siafoundation/sia/wasm',
},
},
})Next.js
// next.config.ts
const nextConfig = {
webpack: (config) => {
config.resolve.alias['sia-wasm'] = require.resolve('@siafoundation/sia/wasm')
config.experiments = { ...config.experiments, asyncWebAssembly: true }
return config
},
}
export default nextConfigTypeScript
Add to your tsconfig.json so the sia-wasm alias resolves types:
{
"compilerOptions": {
"paths": {
"sia-wasm": ["./node_modules/@siafoundation/sia/wasm/sia.d.ts"]
}
}
}Usage
Initialize
Call initSia() once before using any other SDK functions:
import { initSia } from '@siafoundation/sia'
await initSia()Authentication
Authentication is a two-phase flow: first connect your app to an indexer, then register with a recovery phrase. On subsequent visits, reconnect using the stored app key.
New user
import {
Builder,
SiaClient,
generateRecoveryPhrase,
toHex,
} from '@siafoundation/sia'
const indexerUrl = 'https://app.sia.storage'
// 1. Request a connection from the indexer
const builder = new Builder(indexerUrl)
await builder.requestConnection(JSON.stringify({
appID: appKeyHex, // 32-byte hex app identifier
name: 'My App',
description: 'A decentralized storage app',
serviceURL: 'https://myapp.com',
}))
// 2. Direct the user to approve the connection
const approvalUrl = builder.responseUrl()
// Show this URL to the user — they visit it to authorize your app
// 3. Wait for the user to approve
await builder.waitForApproval()
// 4. Register with a recovery phrase to get an authenticated SDK
const phrase = generateRecoveryPhrase()
// Display the phrase for the user to save
const sdk = await builder.register(phrase)
// 5. Wrap in a SiaClient for upload/download
const client = new SiaClient(sdk, indexerUrl)
// 6. Export and persist the app key for reconnection
const appKey = sdk.appKey()
const keyHex = toHex(appKey.export())
// Store keyHex in localStorage or similarCORS fallback: If
requestConnectionfails due to CORS, you can have the user run the POST manually (e.g. via curl) and pass the response tobuilder.setConnectionResponse(appKeyHex, responseJson).
Returning user
On subsequent visits, reconnect using the stored app key — no approval or recovery phrase needed:
import { connect, AppKey, fromHex } from '@siafoundation/sia'
const appKey = new AppKey(fromHex(storedKeyHex))
const client = await connect('https://app.sia.storage', appKey)
// Returns a SiaClient if recognized, or null if the key is unknownUpload
The upload function splits a file into slabs and uploads them in parallel using Web Workers.
import { upload } from '@siafoundation/sia'
const object = await upload(client, file, (progress) => {
console.log(`${progress.phase}: ${progress.slabsComplete}/${progress.slabsTotal} slabs`)
})
// Pin the object to the indexer so it persists
await client.sdk.pinObject(object)For smaller files, you can also upload directly on the main thread without workers:
import { UploadOptions } from '@siafoundation/sia'
const data = new Uint8Array(await file.arrayBuffer())
const opts = new UploadOptions()
const object = await client.sdk.upload(data, opts, (current, total) => {
console.log(`${current}/${total} shards`)
})
await client.sdk.pinObject(object)Download
import { download } from '@siafoundation/sia'
const data = await download(client, object, (progress) => {
console.log(`${progress.phase}: ${progress.bytesComplete}/${progress.bytesTotal} bytes`)
})
// data is a Uint8Array of the decrypted file contentsFor partial reads (e.g. video seeking), use a range download — only the overlapping slabs are fetched:
const chunk = await download(client, object, onProgress, {
range: { offset: 0, length: 1024 * 1024 }, // first 1 MB
})Metadata
Attach and read metadata on objects:
// Write metadata
const meta = new TextEncoder().encode(JSON.stringify({
name: 'photo.jpg',
type: 'image/jpeg',
size: file.size,
}))
object.updateMetadata(meta)
await sdk.updateObjectMetadata(object)
// Read metadata
import { decodeMetadata } from '@siafoundation/sia'
const parsed = decodeMetadata(object.metadata())Sharing
// Create a share link valid for 24 hours
const shareUrl = client.sdk.shareObject(object, Date.now() + 86_400_000)
// Download a shared object
const shared = await client.sdk.sharedObject(shareUrl)
const data = await download(client, shared)API
Top-level exports
| Export | Description |
|--------|-------------|
| initSia() | Initialize the WASM module (call once at startup) |
| SiaClient | Wrapper that bundles an SDK instance with connection details |
| connect(indexerUrl, appKey) | Reconnect a returning user — returns SiaClient \| null |
| upload(client, file, onProgress, numWorkers?) | Parallel multi-worker file upload |
| download(client, object, onProgress?, config?) | Parallel multi-worker download (full or range) |
| generateRecoveryPhrase() | Generate a 12-word BIP-39 recovery phrase |
| validateRecoveryPhrase(phrase) | Validate a recovery phrase |
| fetchHostSettings(address) | Connect to a host via WebTransport and fetch its settings |
| setLogLevel(level) | Control WASM log verbosity ("debug", "info", "warn", "error") |
| toHex(bytes) / fromHex(hex) | Convert between Uint8Array and hex strings |
| decodeMetadata(bytes) | Decode object metadata from bytes to JSON |
SDK
Instance methods available on the sdk property of SiaClient.
| Method | Description |
|--------|-------------|
| upload(data, options, onProgress) | Upload a Uint8Array on the main thread |
| download(object, options, onProgress) | Download an object on the main thread |
| downloadRange(object, offset, length, options, onSector) | Download a byte range — only fetches overlapping slabs |
| streamingUpload(totalSize, options, onProgress) | Start a streaming upload that reads chunks on demand |
| pinObject(object) | Pin an object to the indexer so it persists |
| deleteObject(key) | Delete an object by its hex key |
| object(key) | Retrieve a pinned object by its hex key |
| updateObjectMetadata(object) | Update an object's metadata on the indexer |
| shareObject(object, validUntilMs) | Create a share URL valid until the given timestamp |
| sharedObject(shareUrl) | Retrieve a shared object from a share URL |
| objectEvents(cursor?, limit) | List object events for syncing (cursor-based pagination) |
| account() | Returns account information |
| hosts() | Returns available hosts as a JSON array |
| pruneSlabs() | Prune unused slabs from the indexer |
| appKey() | Returns the AppKey used by this SDK instance |
Builder
Connection builder — authenticate with an indexer.
| Method | Description |
|--------|-------------|
| new Builder(indexerUrl) | Create a builder for the given indexer URL |
| requestConnection(appMetaJson) | Request a new app connection with metadata |
| responseUrl() | Returns the URL the user must visit to authorize |
| waitForApproval() | Polls until the user approves the connection |
| register(phrase) | Register with a recovery phrase — returns an SDK |
| connected(appKey) | Reconnect with an existing AppKey — returns SDK \| null |
| setConnectionResponse(appIdHex, json) | Set a pre-fetched connection response (CORS fallback) |
AppKey
Ed25519 app key for signing and authentication.
| Method | Description |
|--------|-------------|
| new AppKey(key) | Import from a 64-byte keypair or 32-byte seed |
| publicKey() | Returns the hex-encoded public key |
| sign(message) | Sign a message, returns 64-byte signature |
| verifySignature(message, signature) | Verify a signature against a message |
| export() | Export the full 64-byte ed25519 keypair |
PinnedObject
An encrypted object stored on Sia.
| Method | Description |
|--------|-------------|
| id() | Returns the object's hex ID |
| size() | Total size in bytes |
| slabCount() | Number of slabs in the object |
| slabs() | Slab layout with offsets, lengths, and host keys |
| slabLengths() | Data length of each slab as an array |
| metadata() | Returns metadata as a Uint8Array |
| updateMetadata(bytes) | Update the object's metadata |
| seal(appKey) | Serialize for offline storage (returns JSON) |
| PinnedObject.open(appKey, json) | Deserialize a sealed object |
Configuration
| Export | Description |
|--------|-------------|
| UploadOptions | Upload config — dataShards, parityShards, maxInflight, slabDataSize() |
| DownloadOptions | Download config — maxInflight |
| DownloadConfig | Parallel download config — workers, range, maxInflight |
| StreamingUpload | Handle for a streaming upload — pushChunk(data), promise |
License
MIT
