Skip to content
Get started

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/kit

Install @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:

package.json
{
  "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:

.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-corp

Send both with every page that loads the SDK. Sending them with every response is simplest.

next.config.ts
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 page

If 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:

ReasonCause
unsupported-browserThe page is not cross-origin isolated, or the browser lacks a feature the SDK needs. The message names it.
misconfiguredassetsBaseUrl does not end with /, or an endpoint is missing. The message names it.
service-unavailableThe WebAssembly could not be fetched. Check that copy-assets ran.
dependency-nonconformingA served file is not the one this SDK ships. Run copy-assets again.

Troubleshooting covers each one in more depth.

Next

Last updated

On this page