Install and set up
Install the package, serve its files, turn on cross-origin isolation and pass the gateway token.
Your app needs four things before the SDK runs: the package, its files served from your site, two response headers, and a gateway token on mainnet. This page sets up each one, then checks them.
Install the package
pnpm add @umbra/nimbus-solana-web @solana/kitInstall @solana/kit beside it. Every SDK function takes mints and wallets as a kit Address,
which its address function makes from a base58 string.
The SDK runs in the browser only. It needs IndexedDB, Web Workers, Web Locks and WebAssembly threads, which a server render does not have. Load it from code that runs in the browser.
Serve the SDK's files
The package ships the browser files the SDK runs in its assets/ folder:
nimbus_primitives_wasm_bg.wasm, the WebAssembly the SDK's cryptography runs in.prover-worker.js, the worker script each proof runs in.prover/, the WebAssembly prover the worker builds proofs with.witness-graph/, the WebAssembly module that prepares each proof's inputs.
Your app serves these from its own site. A browser starts a module worker only from a script on the page's own origin. No preset hosts them for you.
Copy them on every install
nimbus-solana-web copy-assets <folder> copies every file into a folder you serve. Run it after
every install, so an upgraded SDK never meets old files:
{
"scripts": {
"postinstall": "nimbus-solana-web copy-assets public/nimbus"
}
}You can run it before dev and build instead, as nimbus-solana-web copy-assets public/nimbus && next build. The command creates the folder and overwrites files of the same names. It leaves
other files alone.
The copied files are build output. Add the folder to .gitignore:
/public/nimbus/Point the SDK at them
Pass the folder's URL as assetsBaseUrl. It must end with /, and it may be relative to the
page.
import { } from "@umbra/nimbus-solana-web";
const = await (
{ : "mainnet" },
{ : "/nimbus/" },
);The SDK fetches the WebAssembly from that folder while it loads. Each proof starts a worker from
prover-worker.js, and the worker fetches prover/ and witness-graph/ beside it.
How the SDK checks them
The package records the SHA-256 of every file it ships. The SDK hashes each file before it runs it, and refuses one that does not match:
- The main WebAssembly is hashed before it starts. Its functions are then tested against known answers.
- The worker script is fetched and hashed before a worker starts from it.
- The worker hashes each file it loads in turn.
An altered file throws a NimbusSdkError with reason dependency-nonconforming. The message names
the file and both hashes. The usual cause is files left over from an older SDK, so run
copy-assets again.
sdk.settings.assetPinning says whether this copy of the SDK checks at all. A packed package reads
"pinned". It reads "unpinned" only when the SDK runs from its source, and then nothing is
checked.
if (.. !== "pinned") {
throw new ("This SDK build does not check its files.");
}Turn on cross-origin isolation
The prover shares memory between threads, which a browser allows only on a page served with these two headers:
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corpSend both with every page that loads the SDK. Sending them with every response is simplest.
import type { NextConfig } from "next";
const = [
{ : "Cross-Origin-Opener-Policy", : "same-origin" },
{ : "Cross-Origin-Embedder-Policy", : "require-corp" },
];
const : NextConfig = {
: async () => [{ : "/:path*", : }],
};
export default ;This covers next dev, next start and a deployment on Vercel.
To check, open your app and type this into the browser's console:
.(); // true on an isolated pageIf it prints false, check the headers on the page's own response in the network panel. Headers
on the SDK's files alone are not enough.
Isolation changes what the page may embed. An image or script from another origin loads only if
that origin allows it, with Cross-Origin-Resource-Policy or CORS. Serve token logos from your own
site. Requests the SDK makes with fetch work as before.
The mainnet gateway token
On mainnet, Nimbus's services sit behind a gateway. It accepts a request only when it carries a token issued for your app. Without one, it answers 401.
To get one, email dev@umbraprivacy.com or reach out to any Umbra team member.
The RPC, the indexer and the context service are not behind the gateway. So without a token, a
session still opens, syncs and reads balances. Registration and every payment fail with reason
unauthorized.
Pass it to the session
openNimbusSession takes the token in its gatewayToken option. Pass a string, or a function that
returns one.
import { } from "@umbra/nimbus-solana-web";
const = await (
,
{ : },
{ : "your-gateway-token" },
);The string must be an RFC 6750 bearer token. Anything else throws misconfigured, and the
message never repeats it.
Use a function when your app loads the token at run time, for example from your own backend. Either way, a visitor can read any token their browser holds.
Where it is sent
The SDK sends the token as Authorization: Bearer to the gateway's origins only.
sdk.settings.gatewayEndpoints lists them, each with the name an error uses for it:
for (const { , } of ..) {
.(`${}: ${}`);
}The RPC, the indexer, the context service, your own site and the proving key host never receive
it. The SDK never stores it, logs it, or puts it in an error. closeNimbusSession drops it.
Check it works
Load the SDK and print what it resolved. This is a quick test that the files and headers are right:
import { , } from "@umbra/nimbus-solana-web";
try {
const = await (
{ : "mainnet" },
{ : "/nimbus/" },
);
.(.., ..);
} catch () {
if (()) .(., .);
}It can throw these:
| Reason | Cause |
|---|---|
unsupported-browser | The page is not cross-origin isolated, or the browser lacks a feature the SDK needs. The message names it. |
misconfigured | assetsBaseUrl does not end with /, or an endpoint is missing. The message names it. |
service-unavailable | The WebAssembly could not be fetched. Check that copy-assets ran. |
dependency-nonconforming | A served file is not the one this SDK ships. Run copy-assets again. |
Troubleshooting covers each one in more depth.
Next
Last updated