Skip to content

Environments

There are two: production and your laptop. That is worth stating plainly before anything else.

There is no staging database

bz-backend's local .env uses the production Atlas connection string, and Atlas Network Access includes 223.233.0.0/16 for rotating developer ISP IPs. Local development reads and writes production data.

Set CRON=false before starting a local backend, or your machine becomes a second CSIP and Autopilot executor using the production PRIVATE_KEY_EXECUTOR.

Production

The VM

PropertyValue
Hostbz-prod / 34.14.156.239 (static)
Projectblazpay-latest
Zoneasia-south1-c
SSH userbisht
Key~/.ssh/google_compute_engine
Fallbackgcloud compute ssh bz-prod --zone=asia-south1-c
Process managerPM2

Processes

PM2 appPortDirectoryBranchBuilt on deployLocal checkout
bz-backend5000/home/bisht/bz-backendbishtNewnoyes
bz-agent4000/home/bisht/bz-agentmainyesyes
bz-tokens5003/home/bisht/bz-tokensmainyesno
qube-backend3001/home/bisht/qubetics/backendmainnono

bz-tokens and qube-backend exist only on the server

There is no local copy. deploy.gcp.sh refuses to sync them and tells you so. Editing means editing on the VM, or cloning locally and setting LOCAL_DIR in the script.

PM2 configuration for the backend is bz-backend/ecosystem.config.cjs: one instance, fork mode, autorestart, max_memory_restart: 1G, kill_timeout and listen_timeout both 10 s, logs at /home/bisht/.pm2/logs/bz-backend-{error,out}.log.

Single instance, fork mode — do not change this casually

The backend holds in-process cron schedulers. PM2 cluster mode would start N copies of every cron, so dcaCron would submit the same batch N times with the same executor key. Horizontal scaling requires moving the crons out of the API process first.

Frontend

Netlify, two sites from one repository:

SiteBuild commandOutputConfig
defi.blazpay.comnpm run builddist/netlify.toml
ai.blazpay.comnpm run build:aidist-ai/netlify.ai.toml

Both auto-deploy from the optimized branch.

Data stores

StoreDeploymentNotes
MongoDBAtlas130+ collections. Network Access includes 223.233.0.0/16
RedisManagedQuote cache, fee cache, Autopilot price series, Socket.io adapter, LangGraph checkpoints
PostgreSQL + PGVectorbz-agent knowledge base only

A separate agents database holds the external copy-trading engine's copytrade_* collections. Not part of bz-backend.

Hostnames

HostServesBuilt from
blazpay.comBrand hubSeparate marketing site
defi.blazpay.comBlazpay DEX — trading onlydefi-dex, VITE_SITE=defi
ai.blazpay.comBlazAI — chat onlydefi-dex, VITE_SITE=ai
rewards.blazpay.comRewards, staking, GameFi, DAOSeparate rewards frontend
api.blazpay.comREST + SSE + Socket.iobz-backend on the VM
audit.blazpay.aiPublic share artifactsMigrating to ai.blazpay.com
kima.blazpay.comKima bridge backendServer-only (provider disabled)
qube.blazpay.comQubetics surfaceServer-only
uat.blazpay.comUATCORS-allowlisted; a legacy target

Local

ServicePort
bz-backend5000
bz-agent4000
defi-dex defi shell5173
defi-dex AI shell5174
defi-dex AI preview4174
These docs5199

All of the frontend ports plus 5000, 3000, 3001, 3002 and 3039 are in the backend CORS allowlist, so a local frontend can talk to production API with no CORS change.

Full setup: local development.

CORS allowlist

bz-backend/index.js, with credentials: true:

https://rewards.blazpay.com      https://blazpay.com
https://uat.blazpay.com          https://qube.blazpay.com
https://galxe.com                https://defi.blazpay.com
https://admin.blazpay.com        https://nfts.qubetics.com
https://blazpay.ai               https://www.blazpay.ai
https://audit.blazpay.ai         https://ai.blazpay.com
http://localhost:3000            http://localhost:3001
http://localhost:3002            http://localhost:3039
http://localhost:5000            http://localhost:5173
http://localhost:5174

Socket.io CORS is separate

Configured in services/socket.js, not by the REST corsOptions. That was the one real gap the AI domain split had to close.

Deprecated hosts

For context when reading old scripts or logs:

HostWasSuperseded by
Azure 20.198.176.70Original backend, scripts/deploy.shGCP
Kamatera 194.113.195.167 (bz-backend-us)Previous backend, scripts/deploy.kamatera.shGCP

Both deploy scripts are still in the repo. Do not run them.

Contract environments

ProductMainnetTestnet
BlazpayRelayer22 chains
DCA vaultArbitrum
Autopilot vaultArbitrum, Robinhood ChainArbitrum Sepolia (0x7cA5A09E…)

Testnets cannot test the interesting half

There is no aggregator liquidity on testnets, so a route cannot be quoted — which means quote → build → settle cannot be exercised there. A tiny mainnet trade is more informative. That is what was done to prove the Autopilot loop.

Arbitrum has two hardhat network definitions, and they use different keys

arbitrum signs with BACKEND_PRIVATE_KEY (the Autopilot owner, 0x2Ed0…0162). arbitrum_dca_owner signs with DCA_OWNER_KEY (the executor, 0x5935…0459, which is the DCA vault's owner). Same chain id, different signer.

Pick deliberately: running a DCA upgrade against --network arbitrum fails the onlyOwner check, and running an Autopilot script against arbitrum_dca_owner fails the other way.

Feature flags at runtime

Two mechanisms, and the second is usually the right one:

MechanismChange costUse for
Environment variableSSH + edit .env + restartSecrets, infrastructure, cron master switch
SystemSetting in MongoA database writeLimits, quotas, feature toggles
js
await SystemSetting.getValue('audit_free_limit_per_wallet')      // default 3
await SystemSetting.setValue('audit_free_limit_per_wallet', 5)
await SystemSetting.getByCategory('limits')

Categories: bridge, security, network, fees, limits, general.

Notable env-var switches:

VariableEffect
CRON=falseDisables every cron
DISABLE_BRIDGE_STATUS_CRONJust that one
DISABLE_REDISForce the in-memory fallback
AUTOPILOT_ADDRESS_*Per-chain vault address; empty means the cron skips that chain
*_FORCE_PROXY, *_PROXY_GROUPSRoute rewards/threads traffic to separate services

External dependencies

A partial outage degrades a specific product rather than the platform:

ServiceProduct affected
21 aggregator APIsIndividual routes drop out; the rest still quote
Orderly NetworkPerpetuals entirely — no fallback venue
ZerionPortfolio and wallet card
Etherscan v2Auditor, token safety, approvals, wallet card
Model provider (extraction)Audit, token safety, all extraction nodes
Model provider (conversation)Conversation node (falls back to the extraction provider, more cheaply)
Model provider (drafting)Autopilot policy drafting
CoinMarketCapAutopilot triggers
CoinGeckoFee summary
ZeroDevdApp builder deployment
5 fiat rampsBuy/sell — degrades gracefully
Firebase, AWS SES, Cloudinary, MoralisNotifications, email, images, some balances

Aggregator failures are swallowed by design

AggregatorFactory.getQuotes wraps each provider in a try/catch so one failure cannot kill an SSE stream. The user sees fewer routes, not an error — which also means a permanently-broken provider goes unnoticed. See monitoring.

Internal + developer documentation. Usage figures are point-in-time snapshots — re-verify before publishing them.