Guides
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:
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) originThat'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:
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.
VITE_API_URL=https://api.example.com
VITE_BSV_NETWORK=mainServer: 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 devnpm run build
NODE_ENV=production node --env-file=.env dist/index.jsSERVER_PRIVATE_KEY=7b28…c8
CLIENT_ORIGIN=https://app.example.com
PORT=3000
BSV_NETWORK=mainNetworks#
--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.