> For AI agents: the documentation index is at https://sdk.nimbusprivacy.xyz/llms.txt, and every page is available as Markdown at its URL with .md appended.

# Sessions (/helpers/sessions)

A session is the user's private account, opened on this device. Every payment and every read takes
one. Open it after the wallet connects, keep it while the user is signed in, and close it when they
leave.

In this guide, Alice opens her session, switches to another wallet, and signs out.

## Open a session

Pass the signer from [`openWalletConnection`](https://sdk.nimbusprivacy.xyz/reference/wallets#openwalletconnection):

```ts
import { openNimbusSession } from "@umbra/nimbus-solana-web";

const opened = await openNimbusSession(
  sdk,
  { connectedWallet: signer },
  { gatewayToken },
);

switch (opened.kind) {
  case "opened":
    console.log(opened.session.registration.kind);
    break;
  case "declined":
    console.log("You declined to sign, so your private account stays locked.");
    break;
}
```

The first time, Alice's wallet asks her to sign one message. Signing moves no money and costs
nothing. Her signature unlocks her private account, and the same wallet always unlocks the same
account.

| `kind`     | What happened                              | What to do                                        |
| ---------- | ------------------------------------------ | ------------------------------------------------- |
| `opened`   | The session is open.                       | Check `session.registration`, then let Alice pay. |
| `declined` | Alice refused to sign. Nothing was stored. | Let her try again.                                |

`session.registration.kind` is `registered` or `unregistered`. Alice must
[register](https://sdk.nimbusprivacy.xyz/registration) before her first deposit.

## Options

| Option                 | Default     | What it does                                                                                       |
| ---------------------- | ----------- | -------------------------------------------------------------------------------------------------- |
| `gatewayToken`         | none        | The token your app's requests to Nimbus's gateway carry. A string, or a function that returns one. |
| `signatureSecretStore` | a tab store | Where the unlock signature is kept, below.                                                         |
| `accountIndex`         | `0`         | Which of the wallet's private accounts to open. Each index is a separate balance.                  |

A session opens without a gateway token. But every payment then fails with reason `unauthorized`.
To get one, email [dev@umbraprivacy.com](mailto:dev@umbraprivacy.com) or reach out to any Umbra
team member.

## Choose where the signature is kept

What Alice's signature unlocks is kept in a signature secret store. A reload reads it from there
instead of asking her wallet again. The SDK has three stores:

| Store                                          | Where it keeps the secret    | When Alice signs again                    |
| ---------------------------------------------- | ---------------------------- | ----------------------------------------- |
| `createMemorySignatureSecretStore()`           | This page's memory only      | After every reload                        |
| `createTabSignatureSecretStore()`, the default | The tab's `sessionStorage`   | When she opens a new tab                  |
| `createPersistentSignatureSecretStore()`       | The browser's `localStorage` | When she signs out, or clears the browser |

The tab and persistent stores keep the secret unencrypted. Any script running on your site can read
it, a compromised dependency included. The memory store keeps it off disk, at the cost of a
signature after each reload.
[Private accounts](https://sdk.nimbusprivacy.xyz/explainers/private-account) explains the trade-off in full.

Pass the store you choose when you open the session:

```ts
import {
  createMemorySignatureSecretStore,
  openNimbusSession,
} from "@umbra/nimbus-solana-web";

const signatureSecretStore = createMemorySignatureSecretStore();

const opened = await openNimbusSession(
  sdk,
  { connectedWallet: signer },
  { signatureSecretStore },
);
```

Keep the store, too. You need the same one to sign the user out.

## Switch wallets

A session belongs to one wallet. When Alice switches, close her old session before you open the new
one. Closing clears its keys from memory.

```ts
import {
  closeNimbusSession,
  openNimbusSession,
} from "@umbra/nimbus-solana-web";

closeNimbusSession(session);
const reopened = await openNimbusSession(sdk, { connectedWallet: nextSigner });
if (reopened.kind === "opened") {
  session = reopened.session;
}
```

## Close it when the wallet disconnects

Close the session as soon as the wallet disconnects. A Wallet Standard wallet reports this through
its `standard:events` feature. Its `change` event lists the accounts it still offers:

```ts
import { closeNimbusSession } from "@umbra/nimbus-solana-web";

interface StandardEvents {
  readonly on: (
    event: "change",
    listener: (change: {
      readonly accounts?: readonly { readonly address: string }[];
    }) => void,
  ) => () => void;
}

const events = wallet.features["standard:events"] as StandardEvents | undefined;

const stopListening: (() => void) | undefined = events?.on("change", ({ accounts }) => {
  if (accounts === undefined) return;
  const stillConnected = accounts.some(
    (account) => account.address === session.walletAddress,
  );
  if (!stillConnected) {
    closeNimbusSession(session);
    stopListening?.();
  }
});
```

The same check catches Alice switching accounts inside her wallet. Her old session closes, and you
open a new one for the new account.

## Sign out

`closeNimbusSession` clears the session's keys and gateway token from memory. It leaves the
signature secret store alone, so the next session opens without a signature.

To sign Alice out completely, wipe the store too. Use the store the session was opened with:

```ts
import { closeNimbusSession } from "@umbra/nimbus-solana-web";

closeNimbusSession(session);
await signatureSecretStore.wipeSignatureSecret({
  walletAddress: session.walletAddress,
  cluster: session.sdk.cluster,
  chain: session.sdk.deployment.chain,
});
```

Alice's private balance stays on the device. Her next session finds it there.

## Next

- [Private accounts](https://sdk.nimbusprivacy.xyz/explainers/private-account): What the signature unlocks, and how to choose a store.
- [Registration](https://sdk.nimbusprivacy.xyz/registration): Register before the first deposit.
- [Balances](https://sdk.nimbusprivacy.xyz/helpers/balances): Read what the private balance holds.
- [API reference](https://sdk.nimbusprivacy.xyz/reference/sessions): openNimbusSession, closeNimbusSession and the three stores.


Last updated 21 October 2026
