Skip to content
create-bsv-app

Environment variables

The six variables a generated app reads, their dev defaults, what production insists on, and how to load them.

All configuration lives in one file per app, client/src/bsv/config.ts and server/src/bsv/config.ts. Each reads the environment once, validates it, and exports typed constants. The rest of the code imports those constants instead of reading process.env directly.

The variables#

Client (Vite)#

Variable Default in dev Production What it does
VITE_API_URL http://localhost:3000 required, must be https:// where the API lives; every apiFetch goes here
VITE_BSV_NETWORK test optional main, test or ttn

Server (Node)#

Variable Default in dev Production What it does
SERVER_PRIVATE_KEY random on every start required the server's identity key, used to verify proofs
CLIENT_ORIGIN http://localhost:5173 required, must be https:// the one browser origin CORS allows
PORT 3000 optional 1–65535
BSV_NETWORK test optional main, test or ttn

"Production" means NODE_ENV=production on the server, and vite build on the client (Vite sets import.meta.env.PROD). In production the app refuses to start with a missing or invalid value, instead of quietly falling back to a dev default:

what you'll see
Error: VITE_API_URL is required in production
Error: SERVER_PRIVATE_KEY is required in production
Error: CLIENT_ORIGIN is required in production
Error: CLIENT_ORIGIN must be credential-free HTTPS (or exact HTTP localhost development) origin

That's deliberate. A server that silently picks a random identity, or a client that silently calls localhost, is a bug you'd rather find at deploy time than from your users.

Generate a server key#

Run this from the server/ folder, where @bsv/sdk is installed:

Terminal
node --input-type=module -e "import { PrivateKey } from '@bsv/sdk'; console.log(PrivateKey.fromRandom().toString())"

It prints 64 hex characters. Treat the key like a password: it is your server's identity. If it changes, clients see a different server identity. If it leaks, someone else can impersonate your server.

Deep diveWhy does a dev restart change the server's identity?

With no SERVER_PRIVATE_KEY, the server makes a fresh random key on each start. Clients fetch the identity again (GET /api/identity), so dev keeps working. Anything that pinned the old key, or proofs signed for it in the last two minutes, stops matching. Set a key in dev too once you start pinning.

Load them#

Client: Vite loads client/.env, client/.env.local and client/.env.production for you. Only VITE_ variables reach the browser.

client/.env.production
VITE_API_URL=https://api.example.com
VITE_BSV_NETWORK=main

Server: nothing loads server/.env automatically, and that catches people out. Either export the variables in your shell or host, or tell Node to read the file:

# server/package.json → "dev": "tsx watch --env-file=.env src/index.ts"
npm run dev
server/.env
SERVER_PRIVATE_KEY=7b28…c8
CLIENT_ORIGIN=https://app.example.com
PORT=3000
BSV_NETWORK=main

Networks#

--network sets the default baked into both configs, and the env variables override it per deployment.

Value Network Use it for
test testnet development. Coins are free and worthless
ttn Teratestnet testing against the newer Teranode network
main mainnet production. Real money

Login and signed requests are pure signatures, so they work identically on every network. The network matters once you start creating transactions. See BSV for web devs.