Skip to content
create-bsv-app
NewReplaces @bsv/app

Ship a BSV app in one command.

A React and Express app with , and already wired. You write the features.

npx create-bsv-app@latest my-app --starter full-stack --capabilities wallet-login,signed-requests --yes

v1.1.2 · Node.js 22+ · 16 starters · works with any BRC-100 wallet, like BSV Browser

Examples of apps built on create-bsv-app: a members area with wallet login, an API that uses signed requests instead of API keys, and a shop that sells a download with a wallet payment.

Terminal output of scaffolding and starting a full-stack app
$ npx create-bsv-app@latest my-app \    --starter full-stack \    --capabilities wallet-login,signed-requests --yes◇  Scaffolding project in ~/code/my-app/client...└  Done.Scaffolded my-app (28 file(s) written).Dependencies installed. $ cd my-app && npm run dev  ➜  Local:   http://localhost:5173/server on http://localhost:3000$ 

Example of the demo app after login (illustrative identity key and handle).

You’re logged in

The server verified this identity key, which can also show as a BRC-169 handle.

See it work in 1:13

The real CLI, then the demo app on the full-stack starter: connect a wallet, log in, send a signed request and watch the server refuse a replay. Then the docs.

How to start

  1. 1

    Get a wallet

    Install BSV Browser on desktop or phone. It holds your keys, so your app never does.

  2. 2

    Scaffold

    Pick a starter and capabilities. Dependencies install, providers and routes get wired, and an AGENTS.md is written.

  3. 3

    Run it

    Run npm run dev, open localhost:5173 and connect your wallet. Log in, sign a request, done.

A working app, not a blank page.

After npm run dev you have a client, a server and a wallet flow that already works end to end. Delete the demos when you're ready.

BSV app

Connect a wallet to get started, then try the installed demos.

Connected: 02a1f3c9e8b7d6a5…

Demos

The generated home page after connecting a wallet. Identity key shortened.
  1. Click Connect walletDesktop wallets answer directly; phones pair by QR
  2. Your wallet signs a proofaction: login, valid for 2 minutes, works once
  3. The proof goes to the serverPOST /api/login
  4. Logged in✓ Logged in as 02a1f3c9…
/
Home hub with the connect button and links to each demo
/login
Passwordless login: watch the proof go to POST /api/login and come back verified
/signed-demo
A signed API call to POST /api/echo, bound to its exact body
server/
Express with CORS, GET /api/identity, the login and echo routes, and the mobile QR relay
AGENTS.md
Every generated function, documented for you and your coding agent

Learn it by breaking it.

Live-code the scaffold’s real client, server and wallet in your browser. Sign a request, replay it, watch the server refuse.

Lesson 5 of 9 · Signed requests

Break it

Replay a proof, change a body, switch off the nonce check: see exactly what each defence stops.

Open this lesson

Three building blocks. Zero crypto boilerplate.

Each capability is a handful of readable files in src/bsv. Nothing is hidden in a framework, so you can open them, change them or delete them.

01

Wallet connect

Always on

Connect any BRC-100 wallet. Desktop first, phone as fallback.

--capabilities wallet-connect
client/src/Profile.tsx
import { function useWallet(): WalletStateuseWallet } from './bsv/WalletContext'
import { function ConnectWallet(): React.JSX.ElementConnectWallet } from './bsv/ConnectWallet'

export function function Profile(): React.JSX.ElementProfile() {
  const { const connected: booleanconnected, const identityKey: string | nullidentityKey } = function useWallet(): WalletStateuseWallet()
  if (!const connected: booleanconnected) return <function ConnectWallet(): React.JSX.ElementConnectWallet />
  return <React.JSX.IntrinsicElements.p: React.DetailedHTMLProps<React.HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>p>Hi, {const identityKey: string | nullidentityKey?.String.slice(start?: number, end?: number): stringslice(0, 12)}…</React.JSX.IntrinsicElements.p: React.DetailedHTMLProps<React.HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>p>
}
02

Wallet login

Passwordless login. The wallet signs, the server verifies, you get a trusted identity key.

Wallet login needs no password, email address or reset link: just one signed proof.

On v1.1.2, the hook needs a one-import fix first.

--capabilities wallet-login
client/src/LoginButton.tsx
import { 
function useWalletLogin(opts?: UseWalletLoginOptions): {
    login: () => Promise<{
        identityKey: string;
    }>;
    identityKey: string | null;
    connected: boolean;
}
useWalletLogin
} from './bsv/useWalletLogin'
export function function LoginButton(): React.JSX.ElementLoginButton() { const {
const login: () => Promise<{
    identityKey: string;
}>
login
} =
function useWalletLogin(opts?: UseWalletLoginOptions): {
    login: () => Promise<{
        identityKey: string;
    }>;
    identityKey: string | null;
    connected: boolean;
}
useWalletLogin
()
const const onClick: () => Promise<void>onClick = async () => { const { const identityKey: stringidentityKey } = await
const login: () => Promise<{
    identityKey: string;
}>
login
() // wallet signs, server verifies
var console: Consoleconsole.Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)log('logged in as', const identityKey: stringidentityKey) } return <React.JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>button React.DOMAttributes<HTMLButtonElement>.onClick?: React.MouseEventHandler<HTMLButtonElement> | undefinedonClick={const onClick: () => Promise<void>onClick}>Log in with wallet</React.JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>button> }
03

Signed requests

Authenticate a single API call. The proof is bound to the route and the exact body.

--capabilities signed-requests
const { 
const signedFetch: (url: string, opts: {
    action: string;
    body?: RequestBody;
}) => Promise<Response>
signedFetch
} =
function useSignedRequest(serverIdentityKey?: string): {
    signedFetch: (url: string, opts: {
        action: string;
        body?: RequestBody;
    }) => Promise<Response>;
    connected: boolean;
}
useSignedRequest
()
await
const signedFetch: (url: string, opts: {
    action: string;
    body?: RequestBody;
}) => Promise<Response>
signedFetch
('/api/notes', {
action: stringaction: 'create-note', body?: RequestBody | undefinedbody: { text: stringtext: 'gm' }, })

One proof, one use

A signed request carries a proof bound to its exact body. The server checks it and remembers its nonce, so the same proof sent again is refused.

A signed request, and a replay the server refusesYour app sends a request; your wallet signs a one-time proof for it; the server verifies the proof. Sending the same proof again is refused with 401, because its nonce was already used.Your appsignedFetch('/api/echo')Walletsigns a one-time proofSerververifySignedRequest() 200 · verified 401 · proof already usedSame proof, sent again
How the proofs work

Start clean, or start from a real app.

4 generated starters you compose with capabilities, plus 12 maintained example apps. Each clone records the exact commit it came from.

Complete examples

Maintained apps the CLI clones whole, to read or build on: --starter <id>. Pick a topic, or type what you're building.

12 examples shown

Project templates · Blank, deployable structures to build your own app in.

  • frontend project templatebrc102-frontendThe established frontend project template with support.
  • overlay backend templatebrc102-backendThe established backend template with support.

Payments and commerce · Pay people, charge per use, sell things.

  • PeerPaypeerpayPeer-to-peer BSV payments backed by identity.
  • AtFinderatfinderAn alternative PeerPay interface using the same protocols.
  • MarsCastmarscast-monetized weather data from Mars.
  • MetaMarketmetamarketA marketplace for 3D objects.

Smart contracts · Coins locked by script until conditions are met.

  • MetermeterAn introduction to wallets, contracts, and .
  • CoinflipcoinflipTrustless, provably fair peer-to-peer interactions.
  • LocksmithlocksmithLock coins with a message and unlock them through a wallet.

Overlays and social · Public data the whole network can read and add to.

  • PollrpollrBlockchain polls backed by .
  • PostboardpostboardA public town square of messages built on an .

Wallet storage · Keep app data in the wallet, encrypted.

  • ToDo ListtodoA simple demonstration of wallet and encryption.
Compare all complete examples

Your AI agent can drive it too.

Everything a human can do in the prompts, an agent can do with one JSON file. Every scaffold ships an AGENTS.md, and every docs page is plain Markdown at a stable URL.

  • --file config.json

    One deterministic input and no prompts. Same pipeline as the interactive CLI.

  • AGENTS.md in every project

    Exact APIs, file locations and wiring for each installed capability.

  • /llms.txt and /llms-full.txt

    The whole docs site, ready to paste into a context window.

  • Accept: text/markdown

    Any page, as Markdown. Agents get text, browsers get HTML, same URL.

config.json
{
  "mode": "new",
  "name": "my-app",
  "starter": "full-stack",
  "capabilities": ["wallet-login", "signed-requests"],
  "network": "test"
}
Using create-bsv-app with AI agents

Stop reading. Start shipping.

The quick start goes from nothing to a logged-in wallet in a few minutes.

npx create-bsv-app@latest my-app --starter full-stack --capabilities wallet-login,signed-requests --yes
Open the quick start