Skip to content
create-bsv-app

BSV for web developers

Wallets, identity keys, signatures, BRCs and networks, explained by mapping each one to something you already know from web development.

You don't need to know how a blockchain works to use create-bsv-app. You do need five ideas, because they replace things you'd normally build yourself: user accounts, passwords and sessions.

The cheat sheet#

You know In a BSV app Where it lives
User ID identity key, a public key like 02ab… the user's wallet
Password a signature from the user's wallet never sent, never stored
"Sign in with Google" "Connect wallet" useWallet()
Session cookie a proof per request, or your own JWT after login signedFetch(), login()
Server API key the server's own identity key SERVER_PRIVATE_KEY
Stripe checkout a transaction the wallet creates wallet.createAction() (not in the scaffold yet)

Wallets#

A wallet is an app the user installs, such as BSV Browser (opens in a new tab). It holds their private keys and asks them before doing anything with those keys. Your app never sees a private key. It asks the wallet to sign, encrypt or pay, and the wallet decides whether to.

BRC-100 is the standard interface between apps and wallets, and @bsv/sdk exposes it as WalletInterface. Any BRC-100 wallet works with your app, on desktop or on a phone (paired by QR code).

Identity keys#

When a user connects, you get their identity key: a public key, 66 hex characters starting with 02 or 03. It's stable for that wallet, it's unique, and nobody else can produce signatures for it. In practice it's your user ID.

Signatures vs transactions#

This one trips people up:

  • A signature proves "the owner of key X approved this exact message". It's free, instant, works offline and touches no blockchain. Wallet login and signed requests are signatures. That's why the scaffold costs nothing to run.
  • A transaction moves coins and gets broadcast to the network. It costs a fee (on BSV, a tiny fraction of a cent) and is permanent. The scaffold doesn't create transactions yet. That's your next step.

Your first payment#

When you're ready, the connected wallet can create transactions. The user approves each one in their wallet:

client/src/pay.ts
import { class P2PKHP2PKH, type WalletInterface } from '@bsv/sdk'

/** Ask the user's wallet to send `satoshis` to a BSV address. The wallet shows an approval prompt. */
export async function function pay(wallet: WalletInterface, address: string, satoshis: number): Promise<string | undefined>pay (wallet: WalletInterfacewallet: WalletInterface, address: stringaddress: string, satoshis: numbersatoshis: number): interface Promise<T>Promise<string | undefined> {
  const { const txid: string | undefinedtxid } = await wallet: WalletInterfacewallet.WalletInterface.createAction: (args: CreateActionArgs, originator?: OriginatorDomainNameStringUnder250Bytes) => Promise<CreateActionResult>createAction({
    CreateActionArgs.description: stringdescription: 'Tip the author',
    CreateActionArgs.outputs?: CreateActionOutput[] | undefinedoutputs: [{
      CreateActionOutput.lockingScript: stringlockingScript: new new P2PKH(): P2PKHP2PKH().P2PKH.lock(pubkeyhash: string | number[]): LockingScriptlock(address: stringaddress).Script.toHex(): stringtoHex(),
      CreateActionOutput.satoshis: numbersatoshis,
      CreateActionOutput.outputDescription: stringoutputDescription: 'Tip for the author'
    }]
  })
  return const txid: string | undefinedtxid
}
tsx
const { wallet } = useWallet()
if (wallet) await pay(wallet, recipientAddress, 1000) // 1000 satoshis

This type-checks against the @bsv/sdk version the scaffold installs. Try it on testnet first, with a testnet address and test coins from the BSV Faucet (opens in a new tab) (sign in, paste a testnet address, up to 10 million satoshis a day). Descriptions must be 5–50 characters, or the wallet rejects the action.

Key derivation, briefly#

A wallet doesn't sign everything with one key. For each purpose it derives a fresh key pair from the identity key, a protocol, a key ID and a counterparty (BRC-42 and BRC-43). That's how a login proof made for your server can't be replayed against another server: the counterparty is part of the key. The testing guide proves it with a test.

Networks#

Network --network Coins Use it for
Testnet test (default) free, worthless, from the BSV Faucet (opens in a new tab) building and testing
Teratestnet ttn free, worthless testing against Teranode, BSV's new node software
Mainnet main real production

Signatures work the same everywhere. The network only matters once you create transactions.

BRCs you will meet#

BRCs ("BSV Request for Comments") are BSV's open standards, published in the BRCs repository (opens in a new tab).

BRC What it is Where you'll see it
BRC-100 the app ↔ wallet interface WalletInterface, WalletClient
BRC-103 mutual authentication between peers @bsv/auth proofs
BRC-42 / 43 key derivation, protocol IDs and counterparties every signature
BRC-52 identity certificates (verified attributes on top of a key) a future step for KYC-style needs
BRC-102 deployment info (deployment-info.json) the BRC-102 example starters

Overlays#

Several complete examples (Pollr, Postboard, MetaMarket and others) use overlay services: small servers that watch for transactions matching a topic and index them so apps can look them up. You don't need overlays for login or signed requests. They become relevant when your app's data lives on chain.