Skip to content

Subsidised Gateway

A subsidised gateway allows apps to provide postage stamps on behalf of their users, enabling uploads even when users don’t have their own stamps.

Normally, uploading to Swarm requires a postage stamp — a cryptographic proof of payment for storage. Users must purchase stamps using BZZ tokens, which creates friction for new users.

With a subsidised gateway:

  • The app operator runs a gateway that provides stamps for uploads
  • Users can upload without owning stamps
  • The app operator pays for storage on behalf of users

This is useful for:

  • Onboarding new users who don’t have BZZ tokens yet
  • Free-tier applications where the operator covers storage costs

Pass the subsidisedGatewayUrl option when creating the client:

const client = new SwarmIdClient({
iframeOrigin: 'https://swarm-id.snaha.net',
subsidisedGatewayUrl: 'https://gateway.my-app.com',
metadata: {
name: 'My App',
description: 'A decentralized application',
},
})

The subsidised gateway URL should point to a Bee node that has a postage stamp configured for public use.

Upload availability depends on three things. The uploadMode field in client.connectionInfo indicates which mode is active:

Usable User Stamp Custom Node Subsidised Configured Can Upload Upload Mode Unavailable Reason
Yes — — Yes user-stamp —
No Yes (disabled) No unavailable see below
No No Yes Yes subsidised —
No No No No unavailable see below

All four rows require an authenticated session. Without one uploadMode is unavailable with no reason given, even where a gateway is configured — uploads still need the app secret.

Usable User Stamp

The session resolved a postage batch and its signer key and built a working stamper from them. All three, because the stamper is constructed asynchronously and a failure there is logged rather than thrown — a resolved batch is not on its own enough to upload with. User stamps always take priority over a subsidised gateway.

Custom Node

The user has configured a custom Bee node URL in the identity provider’s network settings. When a custom node is set, the subsidised gateway is silently disabled, so that uploads go to the user’s chosen node. That node must allow the identity site’s origin — see Using Your Own Bee Node.

The check is an exact inequality against the default gateway URL, and it runs in two places: once when the app identifies itself to the proxy, and again whenever network settings arrive or change. The second matters for a storage-partitioned session, whose settings are handed over by the connect popup after the app has identified itself. Reverting to the default URL does not re-enable a gateway already dropped — that takes an app reload.

Subsidised Configured

The app passed a subsidisedGatewayUrl when creating the SwarmIdClient.

When uploadMode is unavailable, ConnectionInfo.uploadUnavailableReason names the first thing that stopped the session, not the last symptom:

Reason Meaning
no-stamp No postage batch or no signer key resolved — most often an account with no drive yet.
stamper-failed The stamp resolved, but the write path did not build. The only place that difference reaches the app.
stamp-expired The stamp resolved, but its stored lifetime has run out. The session refuses to stamp with it; with a subsidised gateway configured, uploads fall back to the gateway instead.
stamp-not-usable The stamp resolved and is not expired, but its stored record says the node does not have the batch or cannot use it. Opening the drive in the identity site refreshes the record from the chain. Falls back to the gateway the same way.
stamp-warming-up The stamp resolved, but its batch is younger than the ten blocks (under a minute on Gnosis) Bee itself waits before stamping with a new batch, so that the storer nodes have it too. The session re-reads the chain until they have passed and onConnectionChange fires then. Falls back to the gateway the same way.

The reason is undefined while the session is unauthenticated, and while canUpload is true.

Read client.connectionInfo to check the current upload mode (synchronous, populated as soon as initialize() resolves):

const info = client.connectionInfo
if (info.canUpload) {
if (info.uploadMode === 'subsidised') {
console.log('Uploads will use the subsidised gateway')
} else {
console.log('Uploads will use your postage stamp')
}
} else {
console.log(`No upload capability available: ${info.uploadUnavailableReason ?? 'not connected'}`)
}

In subsidised mode the upload carries no pin and no tag, whatever the call passed. Pinning means “keep this on the local node”, and the node is the operator’s, not the user’s: the reference gateway proxy strips Swarm-Pin by default, and the public gateway’s CORS preflight rejects the header outright, which a browser reports only as Failed to fetch. Both options apply as usual when the session uploads with the user’s own stamp.

The public gateway is where this bites in practice: its CORS allow-list is short, and a request carrying a Swarm header outside that list fails before it leaves the browser. A Bee node of your own allows every Swarm header.

  1. User’s own stamp — Whenever the session resolved one, it is the stamp that is used
  2. Custom Bee node — If the user has configured a custom Bee node, the subsidised gateway is disabled (even if configured)
  3. Subsidised gateway — Used as a fallback when no user stamp is available

This ensures users retain control over their uploads while providing a fallback for those without stamps.

The Swarm Foundation runs a public gateway that stamps uploads for anyone:

const client = new SwarmIdClient({
iframeOrigin: 'https://swarm-id.snaha.net',
subsidisedGatewayUrl: 'https://api.gateway.ethswarm.org/',
metadata: { name: 'My App' },
})

This is the gateway behind the demo’s “Use subsidised gateway” toggle (off by default), and it is the quickest way to let a first-time user upload during development, a demo, or a hackathon. Two things to know before shipping with it:

  • It is shared and rate limited, on the operator’s terms, with no guarantee for your traffic. Production apps should run their own gateway (below) or expect users to hold a drive.
  • Its CORS allow-list is short — Content-Type, Swarm-Encrypt, Swarm-Postage-Batch-Id, Swarm-Postage-Stamp, Swarm-Deferred-Upload, Swarm-Collection, Swarm-Index-Document — so a request carrying any other Swarm header fails before it leaves the browser, as a bare Failed to fetch. That is why the subsidised path drops pin and tag (above; the pin and tag rows of the API reference say the same). A gateway you run yourself, or a bare Bee node, accepts every Swarm header.

To provide a subsidised gateway for your app:

  1. Run a Bee node with a funded wallet
  2. Purchase a postage stamp with sufficient depth and amount for your expected usage
  3. Deploy a gateway proxy (such as swarm-gateway) in front of your Bee node that automatically injects the postage stamp header for uploads
  4. Expose the gateway via HTTPS
  5. Allow the identity site’s origin in the gateway’s CORS configuration — uploads are made by the Swarm ID iframe, not by the apps you serve, so their origins are not the ones that appear. See Using Your Own Bee Node

The gateway proxy intercepts upload requests and adds the required postage stamp header, so clients can upload without providing their own stamp.

  • The subsidised gateway only provides stamps — it doesn’t have access to user data or keys
  • Uploads are still signed by the user’s app key for feeds and SOCs
  • Consider rate limiting on your gateway to prevent abuse
  • The gateway URL is visible in the client configuration
  • Uploads through your gateway persist only as long as the batch it stamps with. The client cannot read that lifetime, so state it to your users, and top the batch up before it lapses if their content is meant to stay