SwarmIdClient API Reference
The SwarmIdClient is the main class for integrating Swarm ID authentication and storage capabilities into web applications. It enables parent windows to interact with a Swarm ID iframe proxy, providing secure authentication, identity management, and data upload/download functionality to the Swarm decentralized storage network.
Installation
Section titled “Installation”pnpm add @snaha/swarm-idQuick Start
Section titled “Quick Start”import { SwarmIdClient } from '@snaha/swarm-id'
const client = new SwarmIdClient({ iframeOrigin: 'https://swarm-id.example.com', metadata: { name: 'My App', description: 'A decentralized application', }, onConnectionChange: (info) => { console.log('Connection changed:', info.identity?.name, 'canUpload=', info.canUpload) },})
await client.initialize()
// Option 1: Open authentication page manuallyawait client.connect()
// Option 2: Read current connection state and upload if authenticated.// Check `canUpload` too — an authenticated user can still lack upload// capability (no postage stamp, no subsidised gateway).if (client.connectionInfo?.identity && client.connectionInfo.canUpload) { const result = await client.uploadData(new Uint8Array([1, 2, 3])) console.log('Uploaded with reference:', result.reference)}Constructor
Section titled “Constructor”new SwarmIdClient(options)
Section titled “new SwarmIdClient(options)”Creates a new SwarmIdClient instance.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
options.iframeOrigin |
string |
Yes | The origin URL where the Swarm ID proxy iframe is hosted |
options.iframePath |
string |
No | The path to the proxy iframe (defaults to "/proxy") |
options.timeout |
number |
No | Request timeout in milliseconds (defaults to 30000). Bounds each call’s whole round trip through the iframe; a call’s own requestOptions.timeout replaces it for that call |
options.initializationTimeout |
number |
No | Initialization timeout in milliseconds (defaults to 30000) |
options.onConnectionChange |
(info: ConnectionInfo) => void |
No | Callback invoked when the app-visible connection state changes (identity, app key, account, auth, permissions), and whenever the app’s drives change. The current snapshot is also available synchronously via client.connectionInfo. |
options.onDrivesChange |
() => void |
No | Callback invoked only when the app’s drives change: one joins or leaves the grant, the primary moves, a lane moves, a drive expires. Fires once when the client initialises, connected or not, then after the onConnectionChange of the same event. Re-read getDrives() there. |
options.popupMode |
"popup" | "window" |
No | How to display the authentication popup (defaults to "window") |
options.metadata |
AppMetadata |
Yes | Application metadata shown to users during authentication |
options.metadata.name |
string |
Yes | Application name (1-100 characters) |
options.metadata.description |
string |
No | Application description (max 500 characters) |
options.metadata.icon |
string |
No | Application icon as a data URL (SVG or PNG, max 4KB) |
options.buttonConfig |
ButtonConfig |
No | Button configuration for the authentication UI |
options.containerId |
string |
No | ID of container element to place iframe in (optional) |
options.subsidisedGatewayUrl |
string |
No | URL of a subsidised gateway for uploads without user’s postage stamp. See Subsidised Gateway for details. |
options.permissions |
Permissions |
No | What the app asks the user for on the connect screen. permissions.drives is 'all', or a row per drive ({ kind, description?, optional? }), regular ones first (up to 8 rows, description max 120 characters); [] opts out of drives. permissions.dataSharing / permissions.writeAccess (each true) ask for the sharing switches too; see getPermissions(). See getDrives() |
Throws:
invalid-requestif the provided app metadata or permissions are invalid- a plain
Errorif constructed where there is nowindow(a server render)
Example:
const client = new SwarmIdClient({ iframeOrigin: 'https://id.swarm.example.com', timeout: 60000, metadata: { name: 'My app', description: 'A decentralized application built on Swarm', icon: 'data:image/svg+xml;base64,...', }, onConnectionChange: (info) => { updateUI(info) },})Lifecycle Methods
Section titled “Lifecycle Methods”initialize()
Section titled “initialize()”Initializes the client by creating and embedding the proxy iframe.
This method must be called before using any other client methods. It creates a hidden iframe, waits for the proxy to initialize, identifies the parent application to the proxy, and waits for the proxy to signal readiness.
Returns: Promise<void> - Resolves when the client is fully initialized
Throws:
invalid-stateif the client is already initialized, ordestroy()runs before it resolvesinvalid-requestifcontainerIdnames no elementinit-failedif the iframe fails to load, origin validation fails on the proxy side, or the proxy does not respond within the initialization timeout period (configurable viainitializationTimeout, defaults to 30 seconds)
Example:
const client = new SwarmIdClient({ ... })try { await client.initialize() console.log('Client ready')} catch (error) { console.error('Failed to initialize:', error)}destroy()
Section titled “destroy()”Destroys the client and releases all resources.
This method should be called when the client is no longer needed. It performs the following cleanup:
- Rejects every pending request, and a pending
initialize(), with aSwarmIdErrorof codeinvalid-state - Removes the message event listener
- Removes the iframe from the DOM
- Resets the client to an uninitialized state
After calling destroy(), the client instance cannot be reused. Create a new instance if you need to reconnect.
Returns: void
Example:
// Clean up when component unmountsuseEffect(() => { const client = new SwarmIdClient({ ... }) client.initialize()
return () => { client.destroy() }}, [])Authentication Methods
Section titled “Authentication Methods”getAuthIframe()
Section titled “getAuthIframe()”Returns the authentication iframe element.
The iframe displays authentication UI based on the current auth status:
- If not authenticated: shows a “Connect” button
- If authenticated: shows identity info and a “Disconnect” button
The iframe is positioned fixed in the bottom-right corner of the viewport.
Returns: HTMLIFrameElement - The iframe element displaying the authentication UI
Throws:
invalid-stateif the client is not initialized, or the iframe is not available
Example:
const iframe = client.getAuthIframe()// The iframe is already displayed; this returns a reference to itButton Configuration
Section titled “Button Configuration”The authentication button in the iframe can be customized using the buttonConfig option in the constructor. This configuration allows you to customize the button text, colors, and styling.
ButtonConfig Interface:
interface ButtonConfig { connectText?: string disconnectText?: string loadingText?: string backgroundColor?: string color?: string borderRadius?: string fontFamily?: string fontSize?: string fontWeight?: string | number}Example: Customizing Button Configuration
const client = new SwarmIdClient({ iframeOrigin: 'https://swarm-id.example.com', metadata: { name: 'My App', description: 'A decentralized application', }, buttonConfig: { connectText: 'Connect with Swarm', disconnectText: 'Disconnect', loadingText: 'Loading...', backgroundColor: '#4CAF50', color: 'white', borderRadius: '8px', fontFamily: 'Inter, system-ui, sans-serif', fontSize: '15px', fontWeight: '500', }, onConnectionChange: (info) => { console.log('Connection changed:', info.identity?.name, 'canUpload=', info.canUpload) },})
await client.initialize()ButtonConfig Properties:
| Property | Type | Required | Description | Default Value |
|---|---|---|---|---|
connectText |
string |
No | Text for the connect button | “🔐 Login with Swarm ID” |
disconnectText |
string |
No | Text for the disconnect button | “🔓 Disconnect from Swarm ID” |
loadingText |
string |
No | Text shown during loading | “⏳ Loading…” |
backgroundColor |
string |
No | Background color for buttons | #dd7200 (connect), #666 (disconnect) |
color |
string |
No | Text color for buttons | white |
borderRadius |
string |
No | Border radius for buttons and iframe | 0 |
fontFamily |
string |
No | CSS font-family for the button |
the iframe’s own font |
fontSize |
string |
No | CSS font-size for the button |
14px |
fontWeight |
string | number |
No | CSS font-weight for the button |
600 |
Notes:
- Button configuration is passed directly to the constructor, not via postMessage
- The
borderRadiusproperty affects both the buttons and the iframe container - Colors should be valid CSS color strings (e.g.,
'#FF0000','rgb(255, 0, 0)','red') - The typography properties are the only way to make the button match the page’s type: the button is painted inside the cross-origin iframe, where the page’s own CSS cannot reach it
fontFamilytakes a CSS font stack and falls back the way a stack always does — the browser reads it left to right and takes the first family that resolves. It resolves against the IFRAME, so name web-safe or system families (a font the page loads itself is not available inside the iframe)fontSizehas a box to fit into. In the default floating placement the client fixes the iframe at 300×50 px and the proxy page hides the overflow, so a button too tall for 50 px is silently clipped. PasscontainerIdwhen you want a larger size — the app then owns the box- Text properties support plain text or emoji combinations
- The configuration applies to both authentication states (connect/disconnect)
Manual Authentication
Section titled “Manual Authentication”In addition to the iframe-based authentication (via getAuthIframe()), you can manually trigger the authentication flow by calling connect(). This opens the same authentication URL in a new window:
// Open authentication page (default: new window)await client.connect()
// Open as popup windowawait client.connect({ popupMode: 'popup' })When to use connect():
- When you want to trigger authentication from a custom UI element
- When you prefer the authentication to open in a new window instead of an iframe
Authentication Flow:
- User calls
client.connect()or clicks the iframe button - Authentication page opens with the app metadata
- User authenticates with their Swarm ID
- Authentication state is shared with the iframe proxy
onConnectionChangecallback fires with the new connection snapshot
checkAuthStatus()
Section titled “checkAuthStatus()”Checks the current authentication status with the Swarm ID proxy.
Returns: Promise<AuthStatus> - The authentication status object
| Property | Type | Description |
|---|---|---|
authenticated |
boolean |
Whether the user is currently authenticated |
origin |
string | undefined |
The origin that authenticated (if authenticated) |
beeApiUrl |
string | undefined |
The Bee API URL configured in the identity provider |
Throws:
Errorif the client is not initializedErrorif the request times out
Example:
const status = await client.checkAuthStatus()if (status.authenticated) { console.log('Authenticated from:', status.origin)}connect(options?)
Section titled “connect(options?)”Opens Swarm ID authentication page in a new window.
This method creates the same authentication URL as used by the iframe proxy and opens it in a new browser window. The user can authenticate with their Swarm ID, and the resulting authentication will be available to the client when they return.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
options |
ConnectOptions |
No | Configuration options for the authentication window |
options.popupMode |
"window" | "popup" |
No | Whether to open as a popup window (“popup”) or full window (“window”, default) |
ConnectOptions Interface:
interface ConnectOptions { popupMode?: 'popup' | 'window' // Default: "window"}Returns: Promise<void> - Resolves when the authentication popup has been opened
Throws:
invalid-stateif the client is not initializedpopup-blockedif the browser blocks the popup
Example:
const client = new SwarmIdClient({ ... })await client.initialize()
// Open authentication page (default: new window)await client.connect()
// Open as popup windowawait client.connect({ popupMode: 'popup' })disconnect()
Section titled “disconnect()”Disconnects the current session and clears authentication data.
After disconnection, the user will need to re-authenticate to perform uploads or access identity-related features. The onConnectionChange callback will fire with an un-authenticated snapshot (no identity, canUpload: false, uploadMode: "unavailable"). Note that canUpload is reported as false even when a subsidised gateway is configured, because uploads still require an authenticated app secret.
Returns: Promise<void> - Resolves when disconnection is complete
Throws:
invalid-stateif the client is not initializedinternalif the proxy reports the disconnect failedtimeoutif the request times out
Example:
await client.disconnect()console.log('User logged out')deriveAppSecret(label)
Section titled “deriveAppSecret(label)”Derive 32 bytes of app material from this app’s secret and a label of the app’s choosing. The same account, app and label give the same bytes on every device, and nobody without the account can guess them. Use it to seed a feed topic or other app material without exposing the app’s public key.
Parameters:
| Parameter | Type | Description |
|---|---|---|
label |
string |
Domain separator chosen by the app, e.g. "state-feed" |
Returns: Promise<Uint8Array> — the derived 32 bytes
Throws: Error if the client is not initialized or not authenticated
const seed = await client.deriveAppSecret('state-feed')const topic = keccak256(seed) // a stable feed topic nobody else can computeconnectionInfo (property)
Section titled “connectionInfo (property)”Synchronous accessor for the current connection state. Populated by the proxy as soon as it is ready and refreshed automatically whenever any of the derived fields change (identity rename, default-stamp change, account migration, postage stamp purchased, auth transitions). Subscribe to onConnectionChange to react to updates.
Type: ConnectionInfo
Throws: invalid-state if initialize() has not resolved yet.
| Property | Type | Description |
|---|---|---|
canUpload |
boolean |
Whether the user can upload data |
uploadMode |
"user-stamp" | "subsidised" | "unavailable" |
How uploads will be handled. See Upload Availability |
uploadUnavailableReason |
"no-stamp" | "stamper-failed" | "stamp-expired" | "stamp-not-usable" | "stamp-warming-up" | undefined |
Why uploadMode is "unavailable"; absent whenever uploads work. See Why Uploads Are Unavailable |
storagePartitioned |
boolean | undefined |
true when the browser gave the proxy iframe its own partitioned store (Safari ITP, strict privacy settings). Reported for UI messaging; it does not decide whether you can upload |
identity |
object | undefined |
The connected identity details (if authenticated) |
identity.id |
string |
Unique identifier for the identity |
identity.name |
string |
Display name of the identity |
identity.address |
string |
Ethereum address associated with the identity |
identity.publicKey |
string | undefined |
Public key of the identity |
identity.sharingPublicKey |
string | undefined |
The account-wide ACT key: grant to it to share with this person in every app they use. Absent while this app’s data sharing is off (see permissions). See Which key do I grant to? |
identity.postageSignerAddress |
string | undefined |
The address the account’s postage signer stamps with, bare hex like address. An app that funds storage for the account buys a batch with it as createBatch’s owner; the account then adopts the batch in the identity site (Storage, Add drive, Use existing batch) with its own key. Knowing the address lets an app buy for the account, never spend for it |
identity.avatar |
Avatar |
Ready-to-render account avatar. See Avatars |
appKey |
object | undefined |
The app-specific key details |
appKey.address |
string |
Ethereum address of the app key |
appKey.publicKey |
string |
Public key of the app key |
permissions |
{ dataSharing: boolean, writeAccess: boolean } | undefined |
What this app may do with the sharing key, as getPermissions() reports it; refreshed when the user flips a switch in Swarm ID. Absent without a session |
Example:
const info = client.connectionInfoif (info.canUpload) { if (info.uploadMode === 'subsidised') { console.log('Uploads will use the subsidised gateway') } else { console.log('Uploads will use your postage stamp') }} else if (info.uploadUnavailableReason === 'no-stamp') { console.log('No drive yet — buy one to upload')} else if (info.uploadUnavailableReason === 'stamper-failed') { console.log('A drive is attached, but the write path did not build')} else { console.log('Not connected yet')}getPostageBatch()
Section titled “getPostageBatch()”Gets the app’s primary drive: the one a write goes to when it names none. The app’s other drives, if it was granted any, are listed by getDrives().
Returns information about the postage stamp behind it, including batch ID, utilization, depth, and TTL.
Returns: Promise<PostageBatch | undefined> - The postage batch info, or undefined if none is configured
| Property | Type | Description |
|---|---|---|
batchID |
string |
The batch ID (64 hex characters) |
utilization |
number |
How full the fullest bucket is, from 0 to 1, of what one partition may write into it; not Bee’s fill count. Kept from the last write, in the partition it went to, so it can trail another device’s writes. A batch stops accepting uploads when one bucket fills, before the nominal 2^depth × 4096 bytes |
usable |
boolean |
Whether the batch is usable |
label |
string |
Human-readable label |
depth |
number |
Batch depth |
amount |
string |
Batch amount |
bucketDepth |
number |
Bucket depth |
blockNumber |
number |
Block number when created |
immutableFlag |
boolean |
Whether the batch is immutable |
exists |
boolean |
Whether the batch exists |
batchTTL |
number | undefined |
Remaining lifetime in seconds, as of batchTTLMeasuredAt |
batchTTLSource |
'chain' | 'bee' | 'estimate' | undefined |
Where batchTTL came from: the PostageStamp contract or the Bee node, both exact, or an estimate from the purchase amount and the current price, which knows nothing of top-ups, dilutes or price moves since. Show an estimate as one |
batchTTLMeasuredAt |
number | undefined |
When batchTTL was measured, in ms since the epoch; present with it. Age the figure from here, or show “checked …” |
Throws:
Errorif the client is not initializedErrorif the request times out
Example:
const batch = await client.getPostageBatch()if (batch) { console.log('Batch ID:', batch.batchID) console.log('Utilization:', batch.utilization) console.log('Depth:', batch.depth) console.log('TTL:', batch.batchTTL)} else { console.log('No postage batch configured')}getDrives()
Section titled “getDrives()”Lists every drive the app was granted in Swarm ID. Each entry carries the fields of getPostageBatch() plus:
| Property | Type | Description |
|---|---|---|
kind |
'regular' | 'scratch' |
A scratch drive overwrites its oldest data once full; its contents carry no warranty |
primary |
boolean |
The drive a write goes to when it names none: the account default for an 'all' grant, otherwise the first granted regular drive. A scratch drive is never primary |
partition |
number | undefined |
The write partition this session holds on the drive, as connectionInfo.partition reports it for the primary: undefined while it holds none: until the drive’s write path is built (at connect for the primary, on the first write that names it for any other), and after an idle release or a disconnect. Every lease change fires onConnectionChange, so re-read the drives there |
Returns: Promise<Drive[]> — empty when the app was granted no drive
Every write takes an optional batchID (in its upload options, or as its own options where there are none) naming one of these drives. A feed update’s payload and its SOC always go to the same drive. Naming a drive outside the grant rejects with drive-not-granted.
Asking for the sharing switches. options.permissions.dataSharing: true / writeAccess: true put the switch on the connect screen when it is not already on for this app — off, for the user to turn on — and a switch turned on is pinned for the app. A switch left off is simply not granted; the app sees what it got from getPermissions() and can ask again later with requestPermissions().
Asking for drives. Without options.permissions.drives the app gets the account’s default drive. With it, the connect screen asks the user for what the app needs: one row per entry, each a select over the user’s drives of that kind with a way to create one there, or a single “full drive access” checkbox for 'all', which also covers drives the user adds later. A row’s description replaces the helper text under it (“Used to store permanent data” / “Used to store streaming data”); an optional row may be left empty, and the app sees what it got from getDrives(). Rows are granted in order, regular drives first, and the first regular one is the primary.
const client = new SwarmIdClient({ // … permissions: { drives: [ { kind: 'regular', description: 'Used to store your messages' }, { kind: 'scratch', optional: true }, ], },})Example:
const scratch = (await client.getDrives()).find((drive) => drive.kind === 'scratch')await client.uploadData(frame, { batchID: scratch?.batchID })getDriveCapacity(options?)
Section titled “getDriveCapacity(options?)”How much a drive holds and has left for this device: the primary, or the one options.batchID names.
A drive is not filled byte by byte. Every chunk lands in one of 65,536 buckets, picked by its address, and a save fails as soon as one of its chunks lands in a bucket that is already full. So how much still fits is a likelihood, reported at DEFAULT_FIT_CONFIDENCE (0.999): leftBytes is a save that fits 999 times in 1000, and sizeBytes is the same figure for an empty drive, which is the size a drive of its depth is sold as. The figures count the bytes you save: the chunks Swarm adds to address them, and a save’s own manifest and feed update, are already taken off. They assume encrypted saves; plain ones fit under 1% more.
A drive is split into partitions, so two devices can write to it at once, and one save goes into the one partition this device writes to: the one it holds, or else the one it would claim first. The figures are that partition’s; partitions breaks the whole drive down.
| Property | Type | Description |
|---|---|---|
partition |
number |
The partition the figures describe |
sizeBytes |
number |
What the partition holds when empty |
usedBytes |
number |
What has been written to it, as raw slots: a save’s added chunks count here too, so leftBytes reaches 0 a little (under 2%) before usedBytes reaches sizeBytes |
leftBytes |
number |
The largest save that still fits, never more than sizeBytes − usedBytes |
partitions |
PartitionCapacity[] |
The same four figures for every partition whose counter could be read, this one included; another device’s partition comes from what it last published to Swarm |
Returns: Promise<DriveCapacity | undefined>, undefined for a scratch drive, which overwrites its oldest data instead of filling. Rejects with not-authenticated without a session, upload-unavailable when the app has no drive, drive-not-granted for a drive outside the grant, and network or timeout when this device’s partition could not be read.
It reads Swarm for the partitions this session does not hold, so call it when you show storage, not on every connection change.
Full, a little still free. usedBytes can pass sizeBytes: sizeBytes is a cautious figure, and a drive usually takes more. leftBytes then reads 0, while a small save may still fit; ask fitProbability().
Example:
const capacity = await client.getDriveCapacity()if (capacity) { const { usedBytes, leftBytes, sizeBytes } = capacity if (usedBytes >= sizeBytes) showFullButSomeFree() else if (leftBytes < sizeBytes / 10) showLow(leftBytes) else show(`About ${format(leftBytes)} left of ${format(sizeBytes)}`)}fitProbability(bytes, options?)
Section titled “fitProbability(bytes, options?)”The chance, from 0 to 1, that a save of bytes fits in a drive. It asks about the partition this device writes to, the same one getDriveCapacity() reports. Always 1 on a scratch drive, which overwrites its oldest data instead.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
bytes |
number |
Yes | The size of the save; a fraction is rounded up, a negative or non-finite size rejects with invalid-request |
options.batchID |
string |
No | The drive, from getDrives(); defaults to the primary |
options.encrypt |
boolean |
No | Whether the save is encrypted, as uploads are by default |
Returns: Promise<number>. Rejects as getDriveCapacity() does.
Example:
import { DEFAULT_FIT_CONFIDENCE } from '@snaha/swarm-id'
if ((await client.fitProbability(file.size)) < DEFAULT_FIT_CONFIDENCE) { showDriveFull()}requestDrives(drives)
Section titled “requestDrives(drives)”Asks the user for more drives while the app is connected — a viewer who starts streaming and now needs a scratch drive, say. It opens the Permissions for <app> popup, which shows only what the app asks for; the user picks a drive for each row, can create one there, or grants nothing. The rows take the same shape as options.permissions.drives at connect, and what the user picks is added to what the app already holds.
Returns: Promise<Drive[]> — the drives the app holds once the popup is closed, as getDrives() reports them. When the user granted nothing, or closed the popup, that is what the app held before.
The popup grants only to an app with a live connection. An app that was tracking the account default (connected with no drive request) holds an explicit list afterwards, with that default as its first drive: it keeps its primary, and no longer follows a later change of the account default.
Throws:
not-authenticatedif the app is not connectedpopup-blockedif the browser blocked the popup — call it from a click, asconnect()
button.onclick = async () => { const drives = await client.requestDrives([{ kind: 'scratch' }]) const scratch = drives.find((drive) => drive.kind === 'scratch') if (!scratch) return showStreamingUnavailable() startStreaming(scratch.batchID)}The user can also change what an app holds from Swarm ID itself: the app’s page under Apps lists every drive the app was granted, and setting one to None takes it away. A write in flight on a drive taken away is aborted.
getPermissions()
Section titled “getPermissions()”What this app may do with the account-wide sharing key (see Which key do I grant to?):
dataSharing— read what was shared with the sharing key, and see it asidentity.sharingPublicKeywriteAccess— publish and manage shares as the identity (publisher: "identity", and adding or revoking grantees of such a share); never on withoutdataSharing
Both are on unless the user switched them off in Swarm ID — for every app (Apps), or for this one (its page under Apps). A call that needs one that is off rejects with permission-denied, with permission naming the switch; check here first to ask before that happens. The same two booleans ride connectionInfo.permissions, so onConnectionChange sees a switch flipped while the app runs.
Returns: Promise<{ dataSharing: boolean, writeAccess: boolean }>
const { dataSharing } = await client.getPermissions()if (!dataSharing) showSharedInboxLocked()requestPermissions(permissions)
Section titled “requestPermissions(permissions)”The general form of requestDrives(): asks the user for drives and the sharing switches in one popup. permissions is { drives?, dataSharing?: true, writeAccess?: true }. The Permissions for <app> page shows only what is asked — the drive rows empty, each switch at its current value — and the user turns on what they allow, or nothing. Asking for writeAccess shows dataSharing too, which it implies.
Returns: Promise<{ dataSharing: boolean, writeAccess: boolean }> — what the app holds once the popup is closed, as getPermissions() reports it. When every switch asked for is already on and no drives were asked for, no popup opens and the call resolves at once.
Throws:
not-authenticatedif the app is not connectedpopup-blockedif the browser blocked the popup — call it from a click, asconnect()
button.onclick = async () => { const { writeAccess } = await client.requestPermissions({ writeAccess: true }) if (!writeAccess) return showSharingUnavailable() await client.actUploadData(data, grantees, { publisher: 'identity' })}Bee Connectivity Methods
Section titled “Bee Connectivity Methods”isBeeConnected()
Section titled “isBeeConnected()”Checks whether the Bee node is reachable.
This method never throws an exception. If the client is not initialized, the request times out, or any other error occurs, it returns false.
Returns: Promise<boolean> - true if the Bee node is reachable, false otherwise
Example:
const connected = await client.isBeeConnected()if (connected) { console.log('Bee node is online')} else { console.log('Bee node is offline')}getNodeInfo()
Section titled “getNodeInfo()”Gets information about the Bee node configuration. Needs a real Bee node: the public gateway has no /node endpoint and this call fails there. Do not gate uploads on it; its purpose is to detect a dev-mode node, where deferred uploads are required. Nothing else tells you: a direct upload against a dev-mode node does not fail with a mode error, it waits out the request timeout and rejects with Request timeout after 30000ms.
This method retrieves the current Bee node’s operating mode and feature flags. Use this to determine if deferred uploads are required (dev mode) or if direct uploads are available (production modes).
Returns: Promise<{ beeMode, chequebookEnabled, swapEnabled }> — an inline object; there is no exported NodeInfo type
| Property | Type | Description |
|---|---|---|
beeMode |
string |
The Bee node operating mode (“dev”, “light”, “full”, “ultra-light”) |
chequebookEnabled |
boolean |
Whether the chequebook is enabled |
swapEnabled |
boolean |
Whether SWAP is enabled |
Throws:
Errorif the client is not initializedErrorif the Bee node is not reachableErrorif the request times out
Example:
const nodeInfo = await client.getNodeInfo()if (nodeInfo.beeMode === 'dev') { // Dev mode requires deferred uploads await client.uploadData(data, { deferred: true })} else { // Production modes can use direct uploads await client.uploadData(data, { deferred: false })}Data Methods
Section titled “Data Methods”uploadData(data, options?, requestOptions?)
Section titled “uploadData(data, options?, requestOptions?)”Uploads raw binary data to the Swarm network. The reference it returns is read back with downloadData, or from a gateway at /bytes/<reference>; it carries no manifest, so downloadFile and /bzz/ cannot resolve it.
The data is uploaded using the authenticated user’s postage stamp. Progress can be tracked via the options.onProgress callback.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
data |
Uint8Array |
Yes | The binary data to upload |
options |
UploadOptions |
No | Upload configuration |
options.batchID |
string |
No | The drive to write to, from getDrives(). Defaults to the primary drive. Every writer takes it |
options.pin |
boolean |
No | Whether to pin the data locally (defaults to false; ignored on a subsidised gateway) |
options.encrypt |
boolean |
No | Whether to encrypt the data (defaults to false). The 128-hex reference then contains the decryption key: a capability, not an address — see Quick Start |
options.tag |
number |
No | Tag ID for tracking upload progress (ignored on a subsidised gateway) |
options.deferred |
boolean |
No | Whether to use deferred upload (defaults to false). A Bee node in dev mode serves only deferred uploads, and a direct upload against one fails as a request timeout, not as a mode error — see getNodeInfo. Not needed on the public gateway |
options.onProgress |
(progress) => void |
No | Callback for tracking upload progress |
requestOptions |
RequestOptions |
No | Request configuration |
requestOptions.timeout |
number |
No | Request timeout in milliseconds for this call. Replaces the client’s default (30 s) for the whole upload — the round trip through the iframe as well as the Bee request; a file of a few MB over a testnet or the public gateway needs more, here or in new SwarmIdClient({ timeout }) |
requestOptions.headers |
Record<string, string> |
No | Custom headers |
requestOptions.endlesslyRetry |
boolean |
No | Whether to retry endlessly |
Returns: Promise<UploadResult>
| Property | Type | Description |
|---|---|---|
reference |
string |
The Swarm reference (hash) of the uploaded data |
tagUid |
number | undefined |
The tag UID if a tag was created |
stampedChunks |
number | undefined |
Chunks this write sent: one per stamp in user-stamp mode, one per chunk the gateway accepted in subsidised mode. Compare it with an estimate to show what a save cost. The partition lease’s own chunks (lock, intent, and the state publish after each write) are not counted: they re-use reserved slots and spend no capacity. Absent from an identity site older than the field |
Throws:
Errorif the client is not initializedErrorif the user is not authenticated or cannot uploadErrorif the request times out
Example:
const data = new TextEncoder().encode('Hello, Swarm!')const result = await client.uploadData(data, { encrypt: true, onProgress: (progress) => { console.log(`Progress: ${progress.processed}/${progress.total}`) },})console.log('Reference:', result.reference)downloadData(reference, options?, requestOptions?)
Section titled “downloadData(reference, options?, requestOptions?)”Downloads raw binary data from the Swarm network: the counterpart of uploadData. Pass an encrypted (128-hex) reference here rather than putting it in a gateway URL, since the reference contains the decryption key.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
reference |
string |
Yes | The Swarm reference (64 or 128 hex chars) |
options |
DownloadOptions |
No | Download configuration |
options.redundancyStrategy |
0-3 |
No | Strategy for handling redundancy |
options.fallback |
boolean |
No | Whether to use fallback retrieval |
options.timeoutMs |
number |
No | Download timeout in milliseconds, carried in the download options. Not the call’s bound — that is requestOptions.timeout below |
options.actPublisher |
Uint8Array | string |
No | ACT publisher for encrypted content |
options.actHistoryAddress |
Uint8Array | string |
No | ACT history address for encrypted content |
options.actTimestamp |
number | string |
No | ACT timestamp for encrypted content |
requestOptions |
RequestOptions |
No | Request configuration |
requestOptions.timeout |
number |
No | Request timeout in milliseconds for this call. Replaces the client’s default (30 s) for the whole round trip, not only the Bee request |
requestOptions.headers |
Record<string, string> |
No | Custom headers |
requestOptions.endlesslyRetry |
boolean |
No | Whether to retry endlessly |
Returns: Promise<Uint8Array> - The downloaded data
Throws:
Errorif the client is not initializedErrorif the reference is not foundErrorif the request times out
Example:
const data = await client.downloadData('a1b2c3...') // 64 char hex referenceconst text = new TextDecoder().decode(data)console.log('Downloaded:', text)File Methods
Section titled “File Methods”uploadFile(file, name?, options?, requestOptions?)
Section titled “uploadFile(file, name?, options?, requestOptions?)”Uploads a file to the Swarm network, wrapped in a manifest that carries its name and content type. The reference is read back with downloadFile. A gateway’s /bzz/<reference>/ is only for a plain reference, uploaded with encrypt: false: the default one carries the key, which never goes in a gateway URL.
Accepts either a File object (from file input) or raw Uint8Array data. When using a File object, the filename is automatically extracted unless explicitly overridden.
Content and manifest are encrypted by default, so the reference is 128 hex characters and carries the key — the same rule as uploadFiles. encryptManifest: false gives a 64-character reference whose manifest anyone can read over encrypted content; encrypt: false leaves everything plain, which is what a /bzz/ gateway link to a public file wants.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
file |
File | Uint8Array |
Yes | The file to upload |
name |
string |
No | Filename (extracted from File object if not provided) |
options |
UploadOptions |
No | Upload configuration (see uploadData) |
options.encrypt |
boolean |
No | Encrypt the file content (defaults to true). false also leaves the manifest plain |
options.encryptManifest |
boolean |
No | Encrypt the manifest too, so the reference carries the key (defaults to true) |
requestOptions |
RequestOptions |
No | Request configuration |
requestOptions.timeout |
number |
No | Request timeout in milliseconds for this call. Replaces the client’s default (30 s) for the whole upload — the round trip through the iframe as well as the Bee request; a file of a few MB over a testnet or the public gateway needs more, here or in new SwarmIdClient({ timeout }) |
requestOptions.headers |
Record<string, string> |
No | Custom headers |
requestOptions.endlesslyRetry |
boolean |
No | Whether to retry endlessly |
Returns: Promise<UploadResult> - Contains reference and optional tagUid
Throws:
Errorif the client is not initializedErrorif the user is not authenticated or cannot uploadErrorif the request times out
Example:
// From file inputconst fileInput = document.querySelector('input[type="file"]')const file = fileInput.files[0]const result = await client.uploadFile(file)
// From Uint8Array with custom nameconst data = new Uint8Array([...])const result = await client.uploadFile(data, 'document.pdf')downloadFile(reference, path?, options?, requestOptions?)
Section titled “downloadFile(reference, path?, options?, requestOptions?)”Downloads a file from the Swarm network: the counterpart of uploadFile. It resolves a manifest, so a reference from uploadData fails here inside the manifest parser; use downloadData for those.
Returns both the file data and its original filename (if available). For manifest references, an optional path can be specified to retrieve a specific file from the manifest.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
reference |
string |
Yes | The Swarm reference of the file |
path |
string |
No | Path within a manifest to retrieve a specific file |
options |
DownloadOptions |
No | Download configuration (see downloadData) |
requestOptions |
RequestOptions |
No | Request configuration |
requestOptions.timeout |
number |
No | Request timeout in milliseconds for this call. Replaces the client’s default (30 s) for the whole round trip, not only the Bee request |
requestOptions.headers |
Record<string, string> |
No | Custom headers |
requestOptions.endlesslyRetry |
boolean |
No | Whether to retry endlessly |
Returns: Promise<FileData>
| Property | Type | Description |
|---|---|---|
name |
string |
The filename |
data |
Uint8Array |
The file contents |
Throws:
Errorif the client is not initializedErrorif the reference is not foundErrorif the request times out
Example:
const file = await client.downloadFile('a1b2c3...')console.log('Filename:', file.name)
// Create download linkconst blob = new Blob([file.data])const url = URL.createObjectURL(blob)uploadFiles(files, options?, requestOptions?)
Section titled “uploadFiles(files, options?, requestOptions?)”Uploads a folder as one manifest: a fork per file with its path and content type. A gateway serves a plain folder, uploaded with encrypt: false, at /bzz/<reference>/<path>; the default reference carries the key and stays out of gateway URLs. Takes what bee-js takes — the FileList of an <input type="file" webkitdirectory> or a dropped directory, a File[], or { path, file } pairs for a folder the app assembled itself. A File’s path is its webkitRelativePath with the picked folder’s own name removed, or its name. Which files to include is the app’s decision; the library uploads what it is given.
Read a folder back with listFiles and downloadData per entry, or one file with downloadFile and its path.
Every file is read into memory before the upload starts. For folders larger than that, upload the files with uploadData and build the manifest with @ethersphere/core-sdk’s MantarayNode.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
files |
FileList | Iterable<File | { path, file: Blob }> |
Yes | The folder’s files |
options |
UploadFilesOptions |
No | Upload configuration |
options.encrypt |
boolean |
No | Encrypt the file content (defaults to true). false also leaves the manifest plain |
options.encryptManifest |
boolean |
No | Encrypt the manifest too, so the root reference carries the key (defaults to true). false gives a 32-byte root reference whose manifest anyone can read, while the content stays encrypted |
options.indexDocument |
string |
No | The file the bare /bzz/<reference>/ serves (website-index-document), e.g. index.html |
options.errorDocument |
string |
No | The file served for a path that is not in the folder (website-error-document) |
options.pin |
boolean |
No | Pin on the user’s own node (defaults to false; ignored on a subsidised gateway) |
options.deferred |
boolean |
No | Whether to use deferred upload (defaults to false); see uploadData |
options.tag |
number |
No | Tag ID for tracking upload progress |
options.onProgress |
(progress) => void |
No | Called once per file with { total, processed } |
options.useWorkers |
boolean |
No | Stamp in a worker pool, as uploadData does |
options.workerCount |
number |
No | Size of that pool |
requestOptions |
RequestOptions |
No | Request configuration; timeout bounds the whole upload, see uploadFile |
Returns: Promise<UploadResult> - Contains reference (the manifest’s) and optional tagUid
Throws:
Errorif the client is not initializedErrorif the user is not authenticated or cannot uploadErrorif the request times out
Example:
// <input type="file" webkitdirectory>const { reference } = await client.uploadFiles(input.files)
// a public website: plain, so a gateway serves it at /bzz/<reference>/await client.uploadFiles(input.files, { encrypt: false, indexDocument: 'index.html',})
// a folder the app assembledawait client.uploadFiles([ { path: 'index.html', file: new Blob([html], { type: 'text/html' }) }, { path: 'assets/app.js', file: new Blob([js], { type: 'text/javascript' }) },])listFiles(reference, requestOptions?)
Section titled “listFiles(reference, requestOptions?)”Lists the files of a manifest — one uploaded with uploadFiles, or any other. Each entry carries the path, the content type, and the reference of the file’s content, which downloadData reads. The index-document fork is not an entry.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
reference |
string |
Yes | The Swarm reference of the manifest |
requestOptions |
RequestOptions |
No | Request configuration |
Returns: Promise<CollectionEntry[]>
| Property | Type | Description |
|---|---|---|
path |
string |
The file’s path inside the folder |
reference |
string |
The reference of its content, for downloadData |
contentType |
string |
Its Content-Type metadata, when the manifest records one |
Throws:
Errorif the client is not initializedErrorif the reference is not a manifest or is not foundErrorif the request times out
Example:
for (const { path, reference } of await client.listFiles(root)) { const data = await client.downloadData(reference) console.log(path, data.length)}Chunk Methods
Section titled “Chunk Methods”uploadChunk(data, options?, requestOptions?)
Section titled “uploadChunk(data, options?, requestOptions?)”Uploads a single chunk to the Swarm network.
Chunks are the fundamental unit of storage in Swarm (4KB each). This method is useful for low-level operations or when implementing custom chunking strategies.
data is the chunk’s payload, not a finished chunk: the proxy builds the content-addressed chunk around it (span header, BMT address) and returns that address. A chunk that already carries its span header is wrapped a second time and stored under an address nothing else computes. To store a folder as a manifest, use uploadFiles; to save a Mantaray tree of your own, use saveMantarayTreeRecursively with uploadData, which hands back the reference each node was stored under.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
data |
Uint8Array |
Yes | The chunk payload, 1 to 4096 bytes |
options |
UploadOptions |
No | Upload configuration (see uploadData) |
requestOptions |
RequestOptions |
No | Request configuration |
requestOptions.timeout |
number |
No | Request timeout in milliseconds for this call. Replaces the client’s default (30 s) for the whole round trip, not only the Bee request |
requestOptions.headers |
Record<string, string> |
No | Custom headers |
requestOptions.endlesslyRetry |
boolean |
No | Whether to retry endlessly |
Returns: Promise<UploadResult> - Contains the reference of the uploaded chunk
Throws:
Errorif the client is not initializedErrorif the user is not authenticated or cannot uploadErrorif the request times out
Example:
const chunk = new Uint8Array(4096) // 4KB chunkchunk.fill(0x42) // Fill with dataconst result = await client.uploadChunk(chunk)console.log('Chunk reference:', result.reference)downloadChunk(reference, options?, requestOptions?)
Section titled “downloadChunk(reference, options?, requestOptions?)”Downloads a single chunk from the Swarm network.
Retrieves a chunk by its reference hash. This method is useful for low-level operations or when implementing custom retrieval strategies.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
reference |
string |
Yes | The Swarm reference of the chunk |
options |
DownloadOptions |
No | Download configuration (see downloadData) |
requestOptions |
RequestOptions |
No | Request configuration |
requestOptions.timeout |
number |
No | Request timeout in milliseconds for this call. Replaces the client’s default (30 s) for the whole round trip, not only the Bee request |
requestOptions.headers |
Record<string, string> |
No | Custom headers |
requestOptions.endlesslyRetry |
boolean |
No | Whether to retry endlessly |
Returns: Promise<Uint8Array> - The chunk data
Throws:
Errorif the client is not initializedErrorif the reference is not foundErrorif the request times out
Example:
const chunk = await client.downloadChunk('a1b2c3...')console.log('Chunk size:', chunk.length)SOC Methods
Section titled “SOC Methods”SOC (Single Owner Chunk) methods provide read/write helpers for SOCs, which are signed chunks owned by an Ethereum address. Uploads via SOCWriter.upload are encrypted by default and return an encryptionKey for later decryption.
makeSOCReader(ownerAddress, requestOptions?)
Section titled “makeSOCReader(ownerAddress, requestOptions?)”Creates an SOC reader bound to the given owner address.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
ownerAddress |
string |
Yes | Ethereum address of the SOC owner |
requestOptions |
RequestOptions |
No | Request configuration |
requestOptions.timeout |
number |
No | Request timeout in milliseconds for this call. Replaces the client’s default (30 s) for the whole round trip, not only the Bee request |
requestOptions.headers |
Record<string, string> |
No | Custom headers |
requestOptions.endlesslyRetry |
boolean |
No | Whether to retry endlessly |
Returns: SOCReader
| Method | Description |
|---|---|
getOwner() |
Resolves the SOC owner address |
download(identifier, encryptionKey) |
Downloads and decrypts an encrypted SOC |
rawDownload(identifier, encryptionKey?) |
Downloads an unencrypted SOC, or decrypts if encryptionKey is provided |
Throws:
Errorif the client is not initializedErrorif the request times out
Example:
const owner = '0x1234...'const reader = client.makeSOCReader(owner)const resolvedOwner = await reader.getOwner()const soc = await reader.download(identifier, encryptionKey)console.log('Payload:', new TextDecoder().decode(soc.payload))makeSOCWriter(signer?, requestOptions?)
Section titled “makeSOCWriter(signer?, requestOptions?)”Creates an SOC writer for uploading and downloading SOCs. If signer is omitted, the proxy uses the app signer.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
signer |
string |
No | Private key hex string for the SOC signer |
requestOptions |
RequestOptions |
No | Request configuration |
requestOptions.timeout |
number |
No | Request timeout in milliseconds for this call. Replaces the client’s default (30 s) for the whole round trip, not only the Bee request |
requestOptions.headers |
Record<string, string> |
No | Custom headers |
requestOptions.endlesslyRetry |
boolean |
No | Whether to retry endlessly |
Returns: SOCWriter
| Method | Description |
|---|---|
getOwner() |
Resolves the SOC owner address |
upload(identifier, data, options?) |
Uploads an encrypted SOC |
rawUpload(identifier, data, options?) |
Uploads an unencrypted SOC |
download(identifier, encryptionKey) |
Downloads and decrypts an encrypted SOC |
rawDownload(identifier, encryptionKey?) |
Downloads an unencrypted SOC, or decrypts if encryptionKey is provided |
Returns (upload): Promise<SocUploadResult>
| Property | Type | Description |
|---|---|---|
reference |
string |
SOC chunk reference |
owner |
string |
SOC owner address |
encryptionKey |
string |
Encryption key for decrypting the SOC |
tagUid |
number |
Upload tag UID, if provided |
stampedChunks |
number | undefined |
Chunks this write sent, as for uploadData() |
Returns (rawUpload): Promise<SocRawUploadResult>
| Property | Type | Description |
|---|---|---|
reference |
string |
SOC chunk reference |
owner |
string |
SOC owner address |
encryptionKey |
string |
Optional; present only if returned by proxy |
tagUid |
number |
Upload tag UID, if provided |
stampedChunks |
number | undefined |
Chunks this write sent, as for uploadData() |
Throws:
Errorif the client is not initializedErrorif the request times out
Example:
const writer = client.makeSOCWriter()const payload = new TextEncoder().encode('Hello SOC')const upload = await writer.upload(identifier, payload)
const owner = await writer.getOwner()const soc = await writer.download(identifier, upload.encryptionKey)console.log('Owner:', soc.owner)SOC Payload Structure (download methods): SingleOwnerChunk
| Property | Type | Description |
|---|---|---|
data |
Uint8Array |
Full SOC chunk data |
identifier |
string |
SOC identifier |
signature |
string |
SOC signature |
span |
number |
Payload span (bytes) |
payload |
Uint8Array |
SOC payload data |
address |
string |
SOC address/reference |
owner |
string |
SOC owner address |
Epoch Feed Methods
Section titled “Epoch Feed Methods”Epoch feed methods provide time-indexed updates backed by Single Owner Chunks (SOCs). Feed updates are addressed by (topic, owner) and can be looked up by timestamp. The interface mirrors sequential feeds with upload and download helpers.
makeEpochFeedReader(options, requestOptions?)
Section titled “makeEpochFeedReader(options, requestOptions?)”Creates a feed reader bound to a topic and optional owner.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
options.topic |
string |
Yes | Feed topic (32-byte hex string) |
options.owner |
string |
No | Feed owner address; if omitted, resolved via proxy |
requestOptions |
RequestOptions |
No | Request configuration |
requestOptions.timeout |
number |
No | Request timeout in milliseconds for this call. Replaces the client’s default (30 s) for the whole round trip, not only the Bee request |
requestOptions.headers |
Record<string, string> |
No | Custom headers |
requestOptions.endlesslyRetry |
boolean |
No | Whether to retry endlessly |
Returns: FeedReader
FeedReader
Section titled “FeedReader”| Method | Description |
|---|---|
getOwner() |
Resolves the feed owner address |
downloadReference(options?) |
Returns the reference for timestamp options.at (requires encryption key) |
downloadPayload(options?) |
Downloads payload data for timestamp options.at (requires encryption key) |
downloadRawReference(options?) |
Returns unencrypted reference for timestamp options.at (for /bzz access) |
downloadRawPayload(options?) |
Downloads unencrypted payload for timestamp options.at (for /bzz access) |
Epoch download options (options):
| Option | Type | Required | Description |
|---|---|---|---|
at |
number | string | bigint |
No | Unix timestamp (seconds); defaults to current time |
after |
number | string | bigint |
No | Hint for latest known update timestamp |
encryptionKey |
Uint8Array | string |
No | Encryption key for encrypted feed updates |
Returns (downloadReference):
| Property | Type | Description |
|---|---|---|
reference |
string | undefined |
Swarm reference (hex) if found |
encryptionKey |
string | undefined |
Encryption key (hex) if reference is encrypted |
Returns (downloadPayload):
| Property | Type | Description |
|---|---|---|
payload |
Uint8Array | undefined |
Payload bytes if found |
reference |
string | undefined |
Swarm reference (hex) if found |
encryptionKey |
string | undefined |
Encryption key (hex) if reference is encrypted |
Example:
const reader = client.makeEpochFeedReader({ topic })const owner = await reader.getOwner()const reference = await reader.downloadReference({ at: BigInt(Math.floor(Date.now() / 1000)) })const payload = await reader.downloadPayload({ at: BigInt(Math.floor(Date.now() / 1000)) })makeEpochFeedWriter(options, requestOptions?)
Section titled “makeEpochFeedWriter(options, requestOptions?)”Creates a feed writer for updating and reading a topic. If signer is omitted, the proxy uses the app signer.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
options.topic |
string |
Yes | Feed topic (32-byte hex string) |
options.signer |
string |
No | Private key hex string for the feed signer |
requestOptions |
RequestOptions |
No | Request configuration |
requestOptions.timeout |
number |
No | Request timeout in milliseconds for this call. Replaces the client’s default (30 s) for the whole round trip, not only the Bee request |
requestOptions.headers |
Record<string, string> |
No | Custom headers |
requestOptions.endlesslyRetry |
boolean |
No | Whether to retry endlessly |
Returns: FeedWriter
FeedWriter
Section titled “FeedWriter”| Method | Description |
|---|---|
getOwner() |
Resolves the feed owner address |
downloadReference(options?) |
Returns the reference for timestamp options.at (requires encryption key) |
downloadPayload(options?) |
Downloads payload data for timestamp options.at (requires encryption key) |
downloadRawReference(options?) |
Returns unencrypted reference for timestamp options.at (for /bzz access) |
downloadRawPayload(options?) |
Downloads unencrypted payload for timestamp options.at (for /bzz access) |
uploadPayload(data, options?) |
Uploads encrypted payload data and updates the feed |
uploadReference(reference, options?) |
Updates the feed with an encrypted reference |
uploadRawPayload(data, options?) |
Uploads unencrypted payload (for /bzz access) |
uploadRawReference(reference, options?) |
Updates the feed with an unencrypted reference (for /bzz access) |
Epoch upload options (options):
| Option | Type | Required | Description |
|---|---|---|---|
at |
number | string | bigint |
No | Unix timestamp (seconds); defaults to current time |
encrypt |
boolean |
No | Whether payload uploads are encrypted (default true) |
uploadOptions |
UploadOptions |
No | Upload configuration (tag, pin, deferred, etc.) |
encryptionKey |
Uint8Array | string |
No | Encryption key for encrypted feed updates |
Returns (uploadPayload / uploadReference / uploadRawPayload / uploadRawReference):
| Property | Type | Description |
|---|---|---|
socAddress |
string |
SOC address for the feed update |
reference |
string |
Swarm reference stored in the feed |
encryptionKey |
string | undefined |
Encryption key (hex) if reference is encrypted |
epoch |
{ start: string; level: number } |
The epoch where the update was stored |
timestamp |
string |
The timestamp used (stringified bigint) |
stampedChunks |
number | undefined |
Chunks this update sent, the payload’s upload included, as for uploadData(); absent when either part is unknown |
Example:
const writer = client.makeEpochFeedWriter({ topic })const upload = await writer.uploadPayload('Hello feed', { at: BigInt(Math.floor(Date.now() / 1000)),})const latest = await writer.downloadReference({ at: BigInt(Math.floor(Date.now() / 1000)) })Sequential Feed Methods
Section titled “Sequential Feed Methods”Sequential feed methods provide index-based updates backed by Single Owner Chunks (SOCs) and use the chunk API only. Payload uploads are encrypted by default; raw uploads allow supplying or omitting an encryption key.
makeSequentialFeedReader(options, requestOptions?)
Section titled “makeSequentialFeedReader(options, requestOptions?)”Creates a sequential feed reader bound to a topic and optional owner.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
options.topic |
string |
Yes | Feed topic (32-byte hex string) |
options.owner |
string |
No | Feed owner address; if omitted, resolved via proxy |
requestOptions |
RequestOptions |
No | Request configuration |
Returns: SequentialFeedReader
FeedReader
Section titled “FeedReader”| Method | Description |
|---|---|
getOwner() |
Resolves the feed owner address |
downloadPayload(encryptionKey, options?) |
Downloads payload data for the given index or timestamp (requires encryption key) |
downloadRawPayload(options?, encryptionKey?) |
Downloads raw payload data (encryption key optional) |
downloadReference(encryptionKey, options?) |
Downloads reference for the given index or timestamp (requires encryption key) |
Sequential download options (options):
| Option | Type | Required | Description |
|---|---|---|---|
index |
number | string | bigint |
No | Feed index to read (0..2^64-1) |
at |
number | string | bigint |
No | Unix timestamp (seconds); resolves nearest update at or before at |
hasTimestamp |
boolean |
No | Whether payloads include a timestamp prefix (default true) |
lookupTimeoutMs |
number |
No | Timeout (ms) for sequential index lookups (default 2000) |
Example:
const reader = client.makeSequentialFeedReader({ topic })const owner = await reader.getOwner()const payload = await reader.downloadPayload(encryptionKey, { index: 0 })makeSequentialFeedWriter(options, requestOptions?)
Section titled “makeSequentialFeedWriter(options, requestOptions?)”Creates a sequential feed writer for updating and reading a topic. If signer is omitted, the proxy uses the app signer.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
options.topic |
string |
Yes | Feed topic (32-byte hex string) |
options.signer |
string |
No | Private key hex string for the feed signer |
requestOptions |
RequestOptions |
No | Request configuration |
Returns: SequentialFeedWriter
FeedWriter
Section titled “FeedWriter”| Method | Description |
|---|---|
getOwner() |
Resolves the feed owner address |
downloadPayload(encryptionKey, options?) |
Downloads payload data for the given index or timestamp (requires encryption key) |
downloadRawPayload(options?, encryptionKey?) |
Downloads raw payload data (encryption key optional) |
downloadReference(encryptionKey, options?) |
Downloads reference for the given index or timestamp (requires encryption key) |
uploadPayload(data, options?) |
Uploads payload data (encrypted) and updates the feed |
uploadRawPayload(data, options?, encryptionKey?) |
Uploads raw payload data (encryption optional) and updates the feed |
uploadReference(reference, options?) |
Uploads a reference (encrypted) and updates the feed |
Sequential upload options (options):
| Option | Type | Required | Description |
|---|---|---|---|
index |
number | string | bigint |
No | Feed index to write (0..2^64-1); defaults to next index |
at |
number | string | bigint |
No | Unix timestamp (seconds); used when hasTimestamp is true |
hasTimestamp |
boolean |
No | Whether payloads include a timestamp prefix (default true) |
lookupTimeoutMs |
number |
No | Timeout (ms) for sequential index lookups (default 2000) |
pin |
boolean |
No | Whether to pin the data locally (ignored on a subsidised gateway) |
encrypt |
boolean |
No | Whether to encrypt the data |
tag |
number |
No | Tag ID for tracking upload progress (ignored on a subsidised gateway) |
deferred |
boolean |
No | Whether to use deferred upload |
Example:
const writer = client.makeSequentialFeedWriter({ topic })const upload = await writer.uploadPayload('Hello feed!', { index: 0 })const payload = await writer.downloadPayload(upload.encryptionKey, { index: 0 })Feed Manifest Methods
Section titled “Feed Manifest Methods”createFeedManifest(topic, options?, requestOptions?)
Section titled “createFeedManifest(topic, options?, requestOptions?)”Creates a feed manifest for accessing feed content via URL.
A feed manifest enables accessing the latest feed content via a URL path (e.g., /bzz/{manifest-reference}/). The manifest stores metadata about the feed including owner, topic, and feed type.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
topic |
string |
Yes | Feed topic (32-byte hex string) |
options |
object |
No | Configuration options |
options.owner |
string |
No | Feed owner address; if omitted, uses app signer |
options.feedType |
"Sequence" | "Epoch" |
No | Feed type (defaults to "Sequence") |
options.uploadOptions |
UploadOptions |
No | Upload configuration (pin, deferred, etc.) |
requestOptions |
RequestOptions |
No | Request configuration |
requestOptions.timeout |
number |
No | Request timeout in milliseconds for this call. Replaces the client’s default (30 s) for the whole round trip, not only the Bee request |
requestOptions.headers |
Record<string, string> |
No | Custom headers |
Returns: Promise<string> - The feed manifest reference (64 hex characters)
Throws:
Errorif the client is not initializedErrorif no owner is provided and no app signer is availableErrorif the request times out
Example:
// Create manifest for a feed (uses app signer as owner)const manifestRef = await client.createFeedManifest(topic)console.log('Feed accessible at /bzz/' + manifestRef)
// Create manifest with explicit ownerconst manifestRef = await client.createFeedManifest(topic, { owner: '0x1234...',})
// Use with feed writerconst writer = client.makeSequentialFeedWriter({ topic })const result = await writer.uploadPayload('Hello feed!')
const manifestRef = await client.createFeedManifest(topic)// Now feed content accessible via /bzz/{manifestRef}/GSOC Methods
Section titled “GSOC Methods”GSOC (Global Single Owner Chunk) methods enable proximity-based message routing on the Swarm network. These methods allow you to mine a private key that produces a SOC address proximate to a target overlay, then send messages using that key.
gsocMine(targetOverlay, identifier, proximity?)
Section titled “gsocMine(targetOverlay, identifier, proximity?)”Mines a private key whose SOC (Single Owner Chunk) address is proximate to a target overlay address.
This is useful for sending messages to specific nodes in the Swarm network based on their overlay address.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
targetOverlay |
string |
Yes | The target overlay address to be proximate to |
identifier |
string |
Yes | The identifier for the SOC |
proximity |
number |
No | The required proximity (number of matching prefix bits) |
Returns: Promise<string> - The mined signer as a hex string (private key)
Throws:
Errorif the client is not initializedErrorif mining failsErrorif the request times out
Example:
const targetOverlay = 'a1b2c3d4e5f6...'const identifier = 'my-channel'const signer = await client.gsocMine(targetOverlay, identifier, 8)console.log('Mined signer:', signer)gsocSend(signer, identifier, data, options?, requestOptions?)
Section titled “gsocSend(signer, identifier, data, options?, requestOptions?)”Sends a GSOC (Global Single Owner Chunk) message using a mined signer.
Use the signer obtained from gsocMine() to send messages that will be routed to nodes with the target overlay address.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
signer |
string |
Yes | The mined signer (private key) from gsocMine() |
identifier |
string |
Yes | The identifier for the SOC (must match the one used in mining) |
data |
Uint8Array |
Yes | The message data to send |
options |
UploadOptions |
No | Upload configuration (see uploadData) |
requestOptions |
RequestOptions |
No | Request configuration |
requestOptions.timeout |
number |
No | Request timeout in milliseconds for this call. Replaces the client’s default (30 s) for the whole round trip, not only the Bee request |
requestOptions.headers |
Record<string, string> |
No | Custom headers |
requestOptions.endlesslyRetry |
boolean |
No | Whether to retry endlessly |
Returns: Promise<UploadResult> - Contains the reference of the uploaded chunk
Throws:
Errorif the client is not initializedErrorif the user is not authenticated or cannot uploadErrorif the request times out
Example:
// First, mine a signer for the target overlayconst targetOverlay = 'a1b2c3d4e5f6...'const identifier = 'my-channel'const signer = await client.gsocMine(targetOverlay, identifier, 8)
// Then send a message using the mined signerconst message = new TextEncoder().encode('Hello, Swarm!')const result = await client.gsocSend(signer, identifier, message)console.log('Message sent with reference:', result.reference)ACT Methods
Section titled “ACT Methods”ACT (Access Control Tries) methods enable encrypted data sharing with specific grantees. Data is encrypted and only users with the appropriate public key can decrypt it.
Which key do I grant to?
Section titled “Which key do I grant to?”A grantee is a compressed public key, and a SwarmID user has two of them:
appKey.publicKey— that user in one app origin. It is derived from the origin, so a share made out to it cannot be read from another origin, not even a dev build of the same app.identity.sharingPublicKey— that user across every app they connect. Use it to share a drive or folder between two apps, or between your dev and production origins.
The proxy tries both of the reader’s keys on download, so nothing changes on the reading side. Trust consequence: anything granted to a sharing key can be read by every app that user connects — the person granting chooses which key to grant to.
By default the ACT is published with the app key, and only that origin can add or revoke grantees afterwards. Pass publisher: "identity" to actUploadData to publish with the sharing key instead, so any of the user’s apps can manage the grantee list.
The user decides which apps get the sharing key. Two switches in Swarm ID, on by default, account-wide or per app: Data sharing (reading a share made out to the sharing key, listing the grantees of one the identity published) and Write access (publishing as the identity, adding or revoking grantees of such a share). A call that needs a switch that is off rejects with permission-denied: on download the proxy still tries both keys, and refuses only when the sharing key is the one the share names, so the error means “ask the user”, not “not a grantee”. Read the switches with getPermissions() and ask with requestPermissions(). While data sharing is off, identity.sharingPublicKey is absent.
actUploadData(data, grantees, options?, requestOptions?)
Section titled “actUploadData(data, grantees, options?, requestOptions?)”Uploads encrypted data to the Swarm network with access control for specific grantees.
The data is encrypted using ACT (Access Control Tries) and can only be downloaded by users who have one of the grantee public keys.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
data |
Uint8Array |
Yes | The binary data to upload |
grantees |
string[] |
Yes | Array of compressed public keys (66 hex chars each) |
options |
ActUploadOptions |
No | Upload configuration |
options.pin |
boolean |
No | Whether to pin the data locally (defaults to false; ignored on a subsidised gateway) |
options.tag |
number |
No | Tag ID for tracking upload progress (ignored on a subsidised gateway) |
options.deferred |
boolean |
No | Whether to use deferred upload (defaults to false) |
options.onProgress |
(progress) => void |
No | Callback for tracking upload progress |
options.publisher |
"app" | "identity" |
No | Key that publishes: this origin’s app key (default) or the account-wide sharing key |
requestOptions |
RequestOptions |
No | Request configuration |
requestOptions.timeout |
number |
No | Request timeout in milliseconds for this call. Replaces the client’s default (30 s) for the whole round trip, not only the Bee request |
requestOptions.headers |
Record<string, string> |
No | Custom headers |
requestOptions.endlesslyRetry |
boolean |
No | Whether to retry endlessly |
Returns: Promise<ActUploadResult>
| Property | Type | Description |
|---|---|---|
encryptedReference |
string |
The encrypted Swarm reference (pass to actDownloadData) |
historyReference |
string |
The history reference (pass to add/revoke grantees) |
granteeListReference |
string |
Reference to the grantee list |
publisherPubKey |
string |
Publisher’s compressed public key (pass to actDownloadData) |
actReference |
string |
The ACT reference |
tagUid |
number | undefined |
The tag UID if a tag was created |
stampedChunks |
number | undefined |
Chunks this write sent, the ACT, history and grantee list included, as for uploadData() |
Throws:
Errorif the client is not initializedErrorif the user is not authenticated or cannot uploadErrorif the request times out
Example:
const data = new TextEncoder().encode('Secret message')
// Grantees are compressed public keys (33 bytes = 66 hex chars)const grantees = [ '02abc123...', // Alice's public key '03def456...', // Bob's public key]
const result = await client.actUploadData(data, grantees, { onProgress: (progress) => { console.log(`Progress: ${progress.processed}/${progress.total}`) },})
console.log('History Reference:', result.historyReference)console.log('Encrypted Reference:', result.encryptedReference)console.log('Publisher Public Key:', result.publisherPubKey)
// Save these for later download:// - encryptedReference, historyReference, publisherPubKeyactDownloadData(encryptedReference, historyReference, publisherPubKey, timestamp?, requestOptions?)
Section titled “actDownloadData(encryptedReference, historyReference, publisherPubKey, timestamp?, requestOptions?)”Downloads and decrypts ACT-protected data from the Swarm network.
The user must have access (their public key must be in the grantee list) to decrypt the data.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
encryptedReference |
string |
Yes | The encrypted reference from actUploadData |
historyReference |
string |
Yes | The history reference from actUploadData |
publisherPubKey |
string |
Yes | Publisher’s compressed public key from actUploadData |
timestamp |
number |
No | Unix timestamp for historical access (optional) |
requestOptions |
RequestOptions |
No | Request configuration |
requestOptions.timeout |
number |
No | Request timeout in milliseconds for this call. Replaces the client’s default (30 s) for the whole round trip, not only the Bee request |
requestOptions.headers |
Record<string, string> |
No | Custom headers |
requestOptions.endlesslyRetry |
boolean |
No | Whether to retry endlessly |
Returns: Promise<Uint8Array> - The decrypted data
Throws:
Errorif the client is not initializedErrorif the user does not have access (not in grantee list)Errorif the reference is not foundErrorif the request times out
Example:
// Using the references from actUploadDataconst data = await client.actDownloadData(encryptedReference, historyReference, publisherPubKey)const text = new TextDecoder().decode(data)console.log('Decrypted:', text)actAddGrantees(historyReference, grantees, options?, requestOptions?)
Section titled “actAddGrantees(historyReference, grantees, options?, requestOptions?)”Adds new grantees to an existing ACT.
This method adds new public keys to the ACT’s access list. The encrypted reference remains the same, but a new history reference is returned that should be used for future operations.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
historyReference |
string |
Yes | The history reference from actUploadData or previous actAddGrantees |
grantees |
string[] |
Yes | Array of compressed public keys to add (66 hex chars each) |
requestOptions |
RequestOptions |
No | Request configuration |
requestOptions.timeout |
number |
No | Request timeout in milliseconds for this call. Replaces the client’s default (30 s) for the whole round trip, not only the Bee request |
requestOptions.headers |
Record<string, string> |
No | Custom headers |
requestOptions.endlesslyRetry |
boolean |
No | Whether to retry endlessly |
Returns: Promise<ActAddGranteesResult>
| Property | Type | Description |
|---|---|---|
historyReference |
string |
New history reference (use for future ops) |
granteeListReference |
string |
Reference to the updated grantee list |
actReference |
string |
The ACT reference |
stampedChunks |
number | undefined |
Chunks this write sent, as for uploadData() |
Throws:
Errorif the client is not initializedErrorif the user is not authenticatedErrorif the request times out
Example:
const newGrantees = [ '02xyz789...', // Charlie's public key]
const result = await client.actAddGrantees(historyReference, newGrantees)console.log('New History Reference:', result.historyReference)// The encrypted reference remains the same// Use the new historyReference for future operationsactRevokeGrantees(historyReference, encryptedReference, revokeGrantees, options?, requestOptions?)
Section titled “actRevokeGrantees(historyReference, encryptedReference, revokeGrantees, options?, requestOptions?)”Revokes access from grantees by removing them from the ACT.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
historyReference |
string |
Yes | The current history reference |
encryptedReference |
string |
Yes | The current encrypted reference |
revokeGrantees |
string[] |
Yes | Array of compressed public keys to revoke (66 hex chars) |
requestOptions |
RequestOptions |
No | Request configuration |
requestOptions.timeout |
number |
No | Request timeout in milliseconds for this call. Replaces the client’s default (30 s) for the whole round trip, not only the Bee request |
requestOptions.headers |
Record<string, string> |
No | Custom headers |
requestOptions.endlesslyRetry |
boolean |
No | Whether to retry endlessly |
Returns: Promise<ActRevokeGranteesResult>
| Property | Type | Description |
|---|---|---|
encryptedReference |
string |
The content reference, unchanged — see the caution above |
historyReference |
string |
New history reference, without the revoked entries |
granteeListReference |
string |
Reference to the updated grantee list |
actReference |
string |
The ACT reference |
stampedChunks |
number | undefined |
Chunks this write sent, as for uploadData() |
Throws:
Errorif the client is not initializedErrorif the user is not authenticatedErrorif the request times out
Example:
const revokeKeys = [ '02abc123...', // Alice's public key (revoke access)]
const result = await client.actRevokeGrantees(historyReference, encryptedReference, revokeKeys)
// The history reference is new — store it, and use it for everything from here on.console.log('New history reference:', result.historyReference)
// The content reference is the same one you passed in. It is not re-encrypted.console.log('Content reference unchanged:', result.encryptedReference === encryptedReference) // trueactGetGrantees(historyReference, requestOptions?)
Section titled “actGetGrantees(historyReference, requestOptions?)”Retrieves the list of current grantees for an ACT.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
historyReference |
string |
Yes | The history reference of the ACT |
requestOptions |
RequestOptions |
No | Request configuration |
requestOptions.timeout |
number |
No | Request timeout in milliseconds for this call. Replaces the client’s default (30 s) for the whole round trip, not only the Bee request |
requestOptions.headers |
Record<string, string> |
No | Custom headers |
requestOptions.endlesslyRetry |
boolean |
No | Whether to retry endlessly |
Returns: Promise<string[]> - Array of compressed public keys (66 hex chars each)
Throws:
Errorif the client is not initializedErrorif the history reference is not foundErrorif the request times out
Example:
const grantees = await client.actGetGrantees(historyReference)console.log('Current grantees:', grantees.length)grantees.forEach((pubKey) => console.log(' -', pubKey))Errors
Section titled “Errors”Every rejection a client call produces is a SwarmIdError. Its message is the text the call always carried; code is the stable part to branch on, and where Bee or the gateway answered, the response is there too:
import { SwarmIdError } from '@snaha/swarm-id'
try { await client.uploadData(data)} catch (error) { if (!(error instanceof SwarmIdError)) throw error switch (error.code) { case 'not-authenticated': return showConnect() case 'upload-unavailable': // error.reason is the same vocabulary as connectionInfo.uploadUnavailableReason return showNoDrive(error.reason) case 'bee-rejected': return showFailed(`${error.status}: ${error.beeMessage ?? error.message}`) case 'timeout': return retry() default: return showFailed(error.message) }}| Code | Meaning | Also set |
|---|---|---|
not-authenticated |
No session for this origin | |
upload-unavailable |
The session refuses to stamp; see Why Uploads Are Unavailable | reason |
drive-not-granted |
The write names a drive this app was not granted; see getDrives() |
|
permission-denied |
The call needs the account-wide sharing key in a way the user has not allowed this app; see getPermissions() |
permission |
partition-contended |
Every partition of the batch is held by other devices and none yielded in time | |
lease-lost |
This device’s partition was reclaimed mid-write | |
bee-rejected |
Bee or the gateway answered with a non-2xx status. A 404 on /bzz and on a chunk mean different things; url — the request’s path, never the node’s host — says which |
status, beeMessage, url |
network |
The request never got a response: DNS, a refused connection, or a CORS preflight the gateway rejected | |
timeout |
A deadline ran out: the proxy’s on a Bee request, or the client’s on the whole round trip (requestOptions.timeout) |
|
invalid-request |
The message or an argument failed validation | |
invalid-state |
The client cannot take the call in its current state: initialize() has not resolved, was already called, or destroy() ran. A call, or an initialize(), still pending when destroy() runs rejects with this too |
|
init-failed |
The proxy iframe did not come up: it failed to load, the proxy refused the origin, or it did not answer within initializationTimeout; initialize() rejects with this |
|
popup-blocked |
The browser blocked the authentication popup connect() opens: not called from a user gesture, or a blocker |
|
internal |
Anything else; the message is preserved |
The instance is built on the client side from the fields the proxy sends — an Error posted through the iframe bridge arrives as a plain Error, so it never crosses it. An app that bundles two copies of the package should branch on error.code rather than instanceof, which is per copy.
import type { // Client configuration ClientOptions, AuthStatus, ConnectionInfo, Avatar, AvatarSource, PostageBatch, Drive, DriveCapacity, PartitionCapacity, DriveKind, DriveRequest, DriveRequirement, Permissions, DriveTargetOptions, FitOptions, AppMetadata, ButtonConfig, RequestOptions,
// Upload/Download UploadOptions, DownloadOptions, UploadResult, FileData, Reference,
// SOC SingleOwnerChunk,
// Feed types FeedReader, FeedWriter, SequentialFeedReader, SequentialFeedWriter, SequentialFeedPayloadResult, SequentialFeedReferenceResult, SequentialFeedUploadResult,
// ACT types ActUploadOptions,
// Errors SwarmIdErrorCode, SwarmIdErrorDetails,} from '@snaha/swarm-id'Avatars
Section titled “Avatars”Every account has an avatar, generated from its identity id — the same image Swarm ID shows for that account, so the user recognises it in both places. It is handed to you as an image at a URL rather than as markup, so the shape still holds if an avatar ever comes from somewhere other than the generator.
It arrives already resolved on every connection snapshot — there is nothing to call:
const identity = client.connectionInfo.identityif (identity) { img.src = identity.avatar.url}Avatar:
| Property | Type | Description |
|---|---|---|
source |
"generated" |
Where the image came from. Only generated, derived from the identity id, exists today |
url |
string |
Renderable as an <img> source; a data: URL for generated avatars |
Branch on source rather than assuming the URL always wraps an inline SVG — that keeps your code correct if another kind of image is ever added.
getAvatar(options?)
Section titled “getAvatar(options?)”The avatar at a specific size. Most apps do not need this — identity.avatar is already resolved and, being vector today, scales to any size with CSS. Reach for it when you need the image rendered at a particular pixel size. It is async so that honouring a size may involve fetching, which anything but the generated avatar would.
Parameters:
| Parameter | Type | Description |
|---|---|---|
options.size |
number |
Width and height in pixels (default 40) |
Returns: Promise<Avatar | undefined> — undefined when no identity is connected
const avatar = await client.getAvatar({ size: 128 })generatedAvatarSvg(seed, size?)
Section titled “generatedAvatarSvg(seed, size?)”The generated avatar as a standalone SVG string, for callers that want to inline the markup instead of pointing an <img> at a URL. Derived from the seed alone: no network access, no other dependency.
Parameters:
| Parameter | Type | Description |
|---|---|---|
seed |
string |
Value the avatar is derived from — use identity.id |
size |
number |
Width and height in pixels (default 40) |
Returns: string — a complete <svg> element
import { generatedAvatarSvg } from '@snaha/swarm-id'
const identity = client.connectionInfo.identityif (identity) { document.getElementById('avatar').innerHTML = generatedAvatarSvg(identity.id, 48)}The same seed always produces the same image, so it is safe to render on every onConnectionChange without caching. generatedAvatar(seed, size?) is also exported, returning the same image as an Avatar with a data: URL.
The identity site’s entry point
Section titled “The identity site’s entry point”Everything above is @snaha/swarm-id, the package root, and it is the whole surface an app needs: a SwarmIdClient, its types, and a few helpers for reading uploads back. The identity site (the trusted domain) imports its own half of the protocol from @snaha/swarm-id/internal: account state sync and the fold, key derivation, the account bus, the proxy iframe. Every function there takes the account record, the master key or derivationKey, none of which ever leaves the trusted domain, so an app cannot call them and nothing on the wire hands them out. That entry is not documented here; the design pages in the repository (docs/Account-State.md, docs/Account-Bus.md) and the Key Derivation page describe what it does. It is in the package’s exports so the identity site can import it, with one more reader: a host that embeds the identity site and writes its localStorage directly, for which Storage Layout names the few schemas and functions to use. Beyond those, it carries no stability promise, its names move whenever the UI’s needs do, and a change there is not a breaking change of the package. Do not build an app on it.