Skip to content

Quick Start

Install the package, create a client, call connect(). That is the whole integration; the rest of this page is what happens around it.

Terminal window
pnpm add @snaha/swarm-id
import { SwarmIdClient } from '@snaha/swarm-id'
// What the connect popup and the identity site show for your app. The icon is
// any data URL under 4 KB, SVG or PNG; an emoji in an SVG will do to start.
const APP_ICON =
'data:image/svg+xml;charset=utf-8,' +
encodeURIComponent(
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 56 56">' +
'<rect width="56" height="56" rx="8" fill="white"/>' +
'<text x="50%" y="50%" font-size="32" dominant-baseline="middle" text-anchor="middle">🐝</text>' +
'</svg>',
)
// 1. Create the client
const client = new SwarmIdClient({
iframeOrigin: 'https://swarm-id.snaha.net',
// A new account has no drive yet. This lets it upload anyway; see the note
// below.
subsidisedGatewayUrl: 'https://api.gateway.ethswarm.org/',
metadata: {
name: 'My app',
description: 'A demo Swarm application',
icon: APP_ICON,
},
onConnectionChange: (info) => {
console.log('Connection changed:', info.identity?.name, 'canUpload=', info.canUpload)
},
})
// 2. Initialize: embeds the hidden iframe and fills client.connectionInfo
await client.initialize()
// 3. Connect. Either let the iframe show its own connect button, or call
// connect() from yours. It opens the popup and resolves when the user is
// through. Keep the iframe mounted for the life of the page: unmounting
// its container on sign-in leaves the popup with nothing to hand the
// session to.
await client.connect()
// 4. The connection state is always available synchronously
const info = client.connectionInfo
// 5. Upload. A connected user can still have canUpload=false: no drive and
// no subsidised gateway means nothing pays for the upload.
if (info.identity && info.canUpload) {
const result = await client.uploadData(new TextEncoder().encode('Hello, Swarm!'))
console.log('Uploaded:', result.reference)
}
// 6. Tear down when the page is done with it
client.destroy()
Option Type Required Description
iframeOrigin string Yes Origin of the Swarm ID iframe, for example https://swarm-id.snaha.net
metadata object No What the connect popup shows about your app
metadata.name string No Your app’s name
metadata.description string No A line about your app
metadata.icon string No Your app’s icon as a data URL, SVG or PNG, under 4 KB
onConnectionChange function No Called when the app-visible connection state changes (identity, stamp, account, auth)
permissions object No What the connect screen asks the user for: drives, and the sharing switches. See below

The full list, including the subsidised gateway and the iframe button’s look, is on the API Reference.

Without permissions the app writes to the user’s default drive and may read and publish with the account’s sharing key: the account’s defaults, which the user can change. An app that needs more says so, and the connect screen asks for it:

const client = new SwarmIdClient({
// …
permissions: {
// One row per drive on the connect screen: a regular drive, and a scratch
// drive the user may leave empty.
drives: [{ kind: 'regular' }, { kind: 'scratch', optional: true }],
// Asked only when the user has it off.
dataSharing: true,
},
})
// What the user granted, now and whenever they change it in Swarm ID
const drives = await client.getDrives()
const { dataSharing, writeAccess } = await client.getPermissions()
// Ask for more later, from a click
const scratch = await client.requestDrives([{ kind: 'scratch' }])

A write names a drive with batchID, or goes to the app’s primary drive. A call that needs a sharing switch the user turned off rejects with permission-denied. All of it, including what the user sees in Swarm ID, is on Drives & Permissions.

Everything below needs a connected user. Call client.connect() or let the iframe button do it.

// Plain
const result = await client.uploadData(data)
// Encrypted
const result = await client.uploadData(data, { encrypt: true })
// With progress, counted in chunks
const result = await client.uploadData(data, {
onProgress: (progress) => console.log(`${progress.processed}/${progress.total} chunks`),
})
// Read it back. An encrypted reference is 128 hex characters instead of 64.
const bytes = await client.downloadData(result.reference)

What uploadData stores is raw bytes with no manifest. Read it back with downloadData, or from a gateway at /bytes/<reference>; downloadFile and /bzz/<reference>/ cannot resolve it.

An encrypted reference is a capability, not an address. The 128-hex reference from { encrypt: true } contains the decryption key, so whoever holds it can read the content. Treat it as a secret: never put it in a public index, an on-chain record or a gateway URL. If something public has to point at the content, publish a hash of the reference and hand the reference itself to the reader over a channel you trust. Every encrypted chunk is padded to 4 KB and its span is encrypted, so the exact byte length is hidden, but the number of chunks is not; if the size of a payload alone says something, pad or bucket it before upload.

uploadFile wraps the bytes in a manifest that carries the filename, so its reference is read back with downloadFile. The two pairs do not cross. uploadFile encrypts by default, and its 128-hex reference carries the key: a gateway’s /bzz/<reference>/ is only for a plain reference, uploaded with encrypt: false.

const result = await client.uploadFile(file, 'myfile.txt')
const fileData = await client.downloadFile(result.reference)
console.log(fileData.name, fileData.data)

A postage batch (a drive in the identity site) is not an identity. If someone hands you a batch id and the private key that owns it, as hackathon gift codes do, create or sign in to a Swarm ID however you like, then attach the batch: Storage → Add drive → Use existing batch, with the batch id and the signer key. From then on every app the account connects can upload with it. On a new device, restore the account from its recovery phrase and the batch comes back with it.

Terminal window
git clone https://github.com/snaha/swarm-id
pnpm install
pnpm dev

Open http://localhost:3500. The demo runs on port 3500, the identity site on 5500, and the account bus signaling server on 5520. No HTTPS, certificates or custom domains: localhost is a secure context.

For a local Bee cluster, tunnels for real-domain testing, and the known dev keys, see Local Development.

These happen on the identity site, not in your app. The user exports an encrypted .swarmid file there, restores it with the recovery phrase, or rebuilds the account from Swarm on a new device. Architecture has the file format and the flows.