A Multimedia Experience

An overview of integrating images, videos, and other media into BSV applications. Explains how UHRP (BRC-26) and overlay networks link off-chain hosting to on-chain file references for immutable, censorship-resistant content, with options for monetization and access control.

Read as Markdown

UHRP (the Universal Hash Resolution Protocol) provides a hash-based reference to files so that multiple hosts can serve the same content, creating a more resilient, efficient, and verifiable storage and delivery model for multimedia and other data.


Why UHRP?

  1. Self-Authenticating Content
    Each UHRP URL is derived from the file’s SHA-256 hash. When someone downloads content by its UHRP URL, they can verify the bytes match that hash, ensuring data integrity without needing external trust in the host.

  2. Resilient & Distributed
    Multiple storage providers can offer the same file, increasing availability and bandwidth. If one go offline, others can still serve the same content.

  3. Lower On-Chain Costs
    You do not need to store large files on-chain. Instead, you store only an advertisement of availability (a small “token” UTXO) while the file itself stays on standard HTTP storage.

  4. Easy to Host & Access
    You can upload your data to any UHRP-supporting storage host (e.g., https://nanostore.babbage.systems) or even self-host your own server. As long as the host is recognized by the UHRP network, your file is available through its UHRP URL.


Part 1: Publishing Content with UHRP

Step 1: Install the BSV TS-SDK

Make sure your Node.js project is set up, then install the TS-SDK that provides the StorageUploader:

1npm install @bsv/sdk
Copied!

Step 2: Configure and Use the StorageUploader

Below is a simplified example showing how you might upload a file. The uploader requests an upload location from a storage server, performs the upload, and returns a UHRP URL referencing your file.

1import { StorageUploader, WalletClient } from '@bsv/sdk'
2
3async function uploadFileExample() {
4  // You’ll typically configure this with a wallet and a storageURL:
5  const uploader = new StorageUploader({
6    storageURL: 'https://nanostore.babbage.systems',  // or your own host
7    wallet: new WalletClient('auto')                  // must implement WalletInterface
8  })
9
10  // 1. Prepare your file data as a byte array
11  const fileData = [/* your file bytes */]
12  
13  // 2. Publish the file
14  const result = await uploader.publishFile({
15    file: {
16      data: fileData,
17      type: 'image/jpeg'  // or other MIME type
18    },
19    retentionPeriod: 60 // host for 60 minutes
20  })
21
22  console.log('Published:', result.published)
23  console.log('UHRP URL:', result.uhrpURL) 
24  // Example: "XUUVZqvzYskUvVfBZsrqXsvyoAUojQcJxDr6hTUwEz1vPLdvc64z"
25  
26  return result.uhrpURL
27}
Copied!

What’s happening?

  1. The uploader calls the storage server’s /upload route to ask for an upload destination and instructions.
  2. After uploading the bytes, a UHRP URL is returned, created by hashing the file data (SHA-256) and base58-check encoding it with a UHRP prefix.

By default, the storage host will publish an “advertisement” transaction on-chain (the UTXO “token”), letting others on the UHRP network know this file is available.


Part 2: Downloading and Verifying Content with UHRP

Using the StorageDownloader, you can resolve a UHRP URL to one or more HTTP endpoints and fetch the file. The downloader validates the file’s hash to ensure authenticity.

1import { StorageDownloader } from '@bsv/sdk'
2
3async function downloadFileExample(uhrpUrl: string) {
4  const downloader = new StorageDownloader({ networkPreset: 'mainnet' })
5
6  // Resolve: Finds the underlying HTTP link(s) from UHRP advertisements
7  const linkedUrls = await downloader.resolve(uhrpUrl)
8  console.log('Resolved URLs:', linkedUrls)
9
10  // Optionally: Download the file’s bytes and verify:
11  const result = await downloader.download(uhrpUrl)
12  console.log('Downloaded content length:', result.data.length)
13  console.log('MIME type:', result.mimeType)
14
15  // result.data is a byte array that you can convert into a Blob, store, or process
16}
Copied!

If multiple hosts serve that UHRP URL, the downloader tries each in turn until it successfully verifies content matching the expected hash.


Part 3: Bringing It All Into a React Frontend

The @bsv/uhrp-react library provides ready-made React components for images and media sources. After installing:

1npm install @bsv/uhrp-react
Copied!

you can use <Img> and <Source> elements to leverage UHRP URLs seamlessly. The library automatically resolves the hash to a valid HTTP location, verifying the file hash behind the scenes. Here’s a short example:

1import React from 'react'
2import { Img, Source } from '@bsv/uhrp-react'
3
4function MyMediaGallery() {
5  return (
6    <div>
7      <h1>My UHRP-based Media</h1>
8
9      {/* Display an image by its UHRP URL */}
10      <Img
11        src="XUUVZqvzYskUvVfBZsrqXsvyoAUojQcJxDr6hTUwEz1vPLdvc64z"
12        alt="Photo stored on UHRP"
13        style={{ maxWidth: '300px' }}
14      />
15
16      {/* Display video content by its UHRP URL */}
17      <video controls width="400">
18        <Source src="XUTEGfCykZ4E8oJhT98keoPTg2Q28Nq4sJJXLf9CYA5CjV7ZsTCV" />
19      </video>
20    </div>
21  )
22}
23
24export default MyMediaGallery
Copied!

Key features of these React components:

  1. Automatic resolution – The components figure out where to load the content from by using a built-in store.
  2. Fallback/Loading – You may provide a loading or fallback element while the content is resolving.
  3. Seamless integration – They behave just like standard <img> or <source> tags after resolution.

Putting It All Together

  1. Create or obtain a wallet that can sign requests.
  2. Configure the StorageUploader to upload files to your chosen UHRP storage server (e.g., https://nanostore.babbage.systems or your own).
  3. Retrieve the resulting UHRP URL from the upload. This is your permanent, self-authenticating reference to the data.
  4. Use the StorageDownloader in your backend or scripts to verify and fetch data from any host that has advertised it.
  5. In your React front end, install @bsv/uhrp-react and simply drop in <Img> or <Source> with your UHRP URLs. The library handles everything else and ensures your content loads from whichever hosting server is available.

This approach provides a scalable and distributed way to store and serve files without locking you into a single hosting provider. Through the UHRP network, you can pay multiple hosts to advertise the data, maximizing redundancy and guaranteeing that content is always served as originally published.

Was this page helpful?