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

# Keeping the device in sync (/explainers/sync)

A wallet app reads a token balance straight from an RPC node. A private balance cannot be read that
way. Only your user's keys can find their notes, so the device finds them and keeps a copy.

A sync brings that copy up to date.

## What a sync does

`syncNoteStore` runs in rounds until it has read everything new:

```mermaid
sequenceDiagram
participant App as Your app
participant SDK
participant Nimbus as Nimbus indexer and Solana
participant Store as This device
App->>SDK: syncNoteStore
loop until caught up
  SDK->>Nimbus: the next page of pool activity
  SDK->>SDK: try your user's keys on each new note
  SDK->>Store: save their notes and spends
end
SDK->>Store: release payments a closed tab left
SDK-->>App: ingested
```

*Each round reads one page of new pool activity and saves what belongs to your user.*

In each round, the SDK reads one page of new notes, spends and records for each pool. It keeps the
notes your user's keys open. It marks their notes as spent once Solana shows them spent.

Last, it frees the notes of any payment whose tab closed before sending it.

A sync downloads every new note, not just your user's. So it never tells the indexer which notes are
theirs.

## When the SDK syncs for you

Every deposit, Private Send and withdrawal syncs first, by default. A followed Private Send or
withdrawal syncs again once it lands. Pass `sync: false` to a payment only if you synced just before.

## When your app should sync

- **On load,** once the session is open, before you show a balance.
- **When the tab regains focus,** since your user may have been paid meanwhile.
- **On an interval** while the page is visible, to pick up incoming payments.

Every tab shares one copy. A sync in one tab updates the balance for all of them.

## Long syncs

A device that has been away a while, or a new device, has a lot to read. Three options keep a sync
in hand:

- **`maxRounds`** stops after that many rounds. The result's `caughtUp` says whether it finished.
- **`signal`** stops it between rounds when an `AbortSignal` fires.
- **`onProgress`** hears about each round, with the slot reached.

The next sync picks up where the last one stopped.

## What this means for your app

- **Show "updating" while a sync runs.** Show the last balance meanwhile, not a spinner in its place.
- **Never block a payment on your own sync.** The payment syncs for itself.
- **Expect `unregistered` before registration.** Until your user registers, there is nothing to read.

* [syncNoteStore](https://sdk.nimbusprivacy.xyz/reference/reads#syncnotestore): Every option and result of a sync.
* [Private balances](https://sdk.nimbusprivacy.xyz/explainers/private-balance): What the device keeps, and why.


Last updated 21 October 2026
