SiaSia Developer Portal

Store an Object

Storing an object is the core action your app will perform. It is two steps: upload, which sends the data to the network, and pin, which saves the upload to the user's account. The SDK encrypts everything on the device before it leaves — the Understand what happened section after the example explains how.

Prerequisites

Before continuing, make sure you have:

  • An App Key returned from a successful connection to an indexer.

Once you have established a successful connection, you’re ready to store your first object.

Example

rust
use sia_storage::{app_id, AppKey, AppMetadata, Builder, Object, UploadOptions}; use std::io::{self, Write}; const INDEXER_URL: &str = "https://sia.storage"; const APP_META: AppMetadata = AppMetadata { // Replace `app_id` with your real 32-byte App ID (hex-encoded, 64 chars). // Generate this ONCE and keep it stable forever for your app. id: app_id!("0000000000000000000000000000000000000000000000000000000000000000"), name: "My App", description: "Demo application", service_url: "https://example.com", logo_url: None, callback_url: None, }; #[tokio::main(flavor = "multi_thread")] async fn main() -> Result<(), Box<dyn std::error::Error>> { // Create a builder that can reconnect using an existing App Key let builder = Builder::new(INDEXER_URL, APP_META)?; // Ask the user for their App Key print!("Enter your App Key (hex): "); io::stdout().flush()?; let mut app_key_hex = String::new(); io::stdin().read_line(&mut app_key_hex)?; let app_key_hex = app_key_hex.trim(); let mut seed = [0u8; 32]; hex::decode_to_slice(app_key_hex, &mut seed)?; let app_key = AppKey::import(seed); // Reconnect using the App Key let sdk = match builder.connected(&app_key).await? { Some(sdk) => sdk, None => { return Err(io::Error::new( io::ErrorKind::PermissionDenied, "invalid App Key", ) .into()) } }; println!("\nApp Connected!"); // Upload "Hello world!" from an in-memory reader let reader = std::io::Cursor::new(b"Hello world!"); println!("\nStarting upload..."); let obj = Object::default(); let obj = sdk.upload(obj, reader, UploadOptions::default()).await?; // Pin the object — without this, the upload is not persisted sdk.pin_object(&obj).await?; println!("\nUpload complete:"); println!(" - Size: {} bytes", obj.size()); println!(" - Object ID: {}", obj.id()); Ok(()) }

Understand what happened

What the SDK did with your data:

How the upload was secured

  • All data was encrypted client-side before it left the device.
  • The encrypted data was erasure-coded into multiple redundant shards.
  • Each shard was uploaded to an independent storage provider located across the globe.
  • The indexer stored only the encrypted object record. It never sees plaintext data.

Encryption keeps data private even if intercepted. Erasure coding keeps it recoverable even if some hosts go offline.

Upload vs. pin

Uploading returns an object handle you can work with immediately (for example, to pin it, share it, or download it later).

In this quickstart flow, upload and pin are separate steps:

  • Upload sends shards to storage providers and builds the object’s layout.
  • Pinning saves the upload to the user’s account: it persists the sealed object record in the indexer and pins the underlying slabs so the object becomes listable, syncable, and eligible for repair.

Pinning encrypts and signs the object (a process called sealing) before sending it to the indexer — you don’t need to seal manually. See Pinning and Objects for more.

The Object ID

The Object ID is a permanent, 32-byte identifier derived from the object’s slab layout, so you can read it directly after upload — and you’ll use it to look the object up and download it later.

Metadata is application-defined and encrypted. See the Object Metadata recipe for details.