Discoverable vs out-of-band notes
Three modes for delivering a note's secret to its recipient.
Discoverable mode
The recipient has registered a viewing key on the keystore. When a sender prepares a transfer, the SDK encrypts the note payload to the recipient's viewing public key (via a fresh ephemeral X25519 keypair and ECDH) and emits it in the on-chain Note event. The event's hint is a fresh random 32-byte value. It is not derived from anyone's keys and carries no targeting information. The recipient's discoverNotes() tries to decrypt Note events with their viewing private key, and a successful decrypt is the "this note is mine" signal.
- UX win: no out-of-band channel needed. Send-and-forget.
- Cost: the recipient paid the one-time keystore registration gas.
- Public surface: the on-chain Note event carries a random 32-byte hint that reveals nothing about sender or recipient. There is no key under which the hint identifies the recipient: discovery is by trial ECDH-decryption, not hint matching.
Out-of-band (OoB) mode
The recipient hasn't registered. The SDK publishes no recipient Note event at all. Only the sender's own change note is announced on-chain. The recipient's noteSecret is returned to the sender off-chain, and the sender must hand it to the recipient directly via a secure channel.
- UX win: no registration required of the recipient. Anyone with an Ethereum address can receive.
- Cost: sender must deliver the secret to the recipient. The note isn't claimable until they do.
- Public surface: only that "some transfer happened", with no recipient hint.
Payment-request mode (recipient per-request key)
A third mode used by payment requests. The recipient generates a per-request derived keypair and includes the derived pubkey inside the payment request itself. The keypair is deterministically derived (HKDF) from the recipient's viewing private key and the paymentId: the same paymentId always re-derives the same keypair, and the viewing private key must stay secret to keep payment-request keys secret. The sender encrypts to that pubkey and writes the request's tag (Poseidon([paymentId])) as the on-chain hint. The recipient finds payments by tag-matching Note events to their open requests, then decrypts each matched event with the request's paymentPrivKey.
- UX win: the recipient doesn't need a keystore entry to receive a targeted payment, and the sender doesn't need any of the recipient's long-lived keys.
- Cost: discovery uses request tags to narrow the event stream, so only matching events are decrypted. Prune claimed requests to keep the tag map small.
Choosing between them
The SDK auto-selects. If the recipient's viewing key is in the keystore it uses discoverable mode, and otherwise it falls back to OoB (opt in with allowOutOfBandFallback: true). Payment-request mode is opted into explicitly by generating a request.
| Scenario | Mode |
|---|---|
| Two regular users who've both registered | Discoverable |
| The recipient hasn't registered, so you deliver the secret directly | OoB |
| Receiving against a payment request | Payment-request mode |