Skip to content

Runtime topology

Hosts, processes, ports, data stores and the realtime layer.

Production

Everything server-side runs on one GCP VM, replacing earlier Azure and Kamatera boxes.

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 appPortDirectoryBranchBuild on deploy
bz-backend5000/home/bisht/bz-backendbishtNewno
bz-agent4000mainyes
bz-tokens5003server-only, no local checkout
qube-backend3001server-only, no local checkout

PM2 config for the backend is bz-backend/ecosystem.config.cjs: single 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 with merged logs and a timestamped format.

Single instance, fork mode — and that is deliberate

The backend holds in-process cron schedulers. Running it in PM2 cluster mode would start N copies of every cron, so dcaCron would submit the same batch N times. If you ever need horizontal scaling, the crons must move out of the API process first.

Frontend

Netlify, two sites from one repo:

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

Auto-deploys from the optimized branch. main is roughly 80 commits behind and effectively stale.

Data stores

MongoDB

PropertyValue
DeploymentMongoDB Atlas
Collections130+ across models/ and v1/models/
DriverMongoose 9
Network accessIncludes 223.233.0.0/16 for rotating developer ISP IPs

Local development points at production Atlas by default. See the warning under local development.

A second database, agents, holds the external copy-trading engine's copytrade_* collections. It is not part of bz-backend.

Redis

Four distinct uses, all on the same instance:

UseKey shapeTTL
Quote cachemeta.id{ data, meta, restProps }5 min
Fee summary cachefees:summary:v15 min
Autopilot price seriesrolling series per tokenrolling window
Socket.io adapterpub/sub channels
LangGraph checkpointer (bz-agent)per session

Local dev falls back to an in-memory Redis stub

That is fine for the API, but it means the quote cache is not shared between processes and the Socket.io Redis adapter is disabled — socket.js checks typeof redisPubClient?.publish === 'function' and silently runs without it.

PostgreSQL + PGVector

Used only by bz-agent, for the vector knowledge base behind the FALLBACK intent. Accessed through pg and @langchain/community.

Realtime

Socket.io on the backend, with a Redis adapter when available.

services/socket.js
  ├─ default namespace     chat + payments (chatAndPay.service.js)
  ├─ priceNamespace        price streaming
  ├─ predictionNamespace   prediction market updates
  └─ poker                 initPokerSocket → pokerSocketHandler.js

Auth is middleware/authSocketMiddleware.js. onlineUsers is tracked in-process.

EventEmitter.defaultMaxListeners is raised to 25

The Redis adapter's Bus adds roughly two listeners per namespace. With five namespaces the Node default of 10 emits a spurious max-listeners warning, so socket.js raises the limit at the top of the file. Do not "fix" that line.

SOCKET_DEBUG_GUIDE.md in bz-backend is the troubleshooting reference. CORS for Socket.io is configured separately from REST CORS, and was the one real gap the AI domain split had to close.

Cron processes

All in-process in bz-backend, started in index.js behind CRON != 'false':

CronFrequencyPurpose
cronBatchDeposit (dcaCron.js)every minuteCSIP execution + auto-claims
autopilotCrontickAutopilot trigger evaluation
attendanceCrondailyStreaks and attendance
discordBoosterCrontickDiscord booster bonuses
initPredictionCron()tickPrediction market settlement
initPriceAlertsCron()tickPrice alert firing
initBridgeStatusCron()tickBridge status reconciliation
proposalStatusJob (daoCron.js)tickDAO proposal lifecycle

Currently commented out in index.js: cronJobService, cronClaimRewards. Full inventory: cron jobs.

Ports summary

PortProcessEnvironment
5000bz-backendprod + local
4000bz-agentprod + local
5003bz-tokensprod only
3001qube-backendprod only
5173defi-dex defi shelllocal dev
5174defi-dex AI shelllocal dev
4174defi-dex AI previewlocal
5199this documentation sitelocal

5173, 5174, 5000, 3000, 3001, 3002 and 3039 are all in the backend CORS allowlist, so local frontends can talk to production API without a CORS change.

Request entry points

app.set('trust proxy', 1) is set, and express.json({ limit: '20mb' }) — the large limit exists for pasted Solidity source and generated dApp HTML.

Every request is logged with timestamp, method, URL, forwarded IP and origin.

External dependencies

A partial outage in any of these degrades a specific product rather than the platform:

ServiceProduct affected if down
21 aggregator APIsIndividual routes drop out of the fan-out; the rest still quote
Orderly NetworkPerpetuals entirely
ZerionPortfolio balances and the wallet card
Etherscan v2Contract auditor, token safety, approval scan, wallet card
Model provider (extraction)Audit, token safety, every extraction node
Model provider (conversation)Conversation node (falls back to the extraction provider)
Model provider (drafting)Autopilot policy drafting
CoinMarketCapAutopilot triggers
CoinGeckoFee summary
ZeroDevdApp builder on-chain deployment
5 fiat rampsBuy/sell — comparison degrades gracefully
FirebasePush notifications
AWS SESEmail
CloudinaryImage uploads
MoralisSome balance lookups

Aggregator failures are swallowed on purpose

AggregatorFactory.getQuotes wraps each provider in a try/catch so one failing API cannot kill the SSE stream. The user sees fewer routes, not an error. That resilience is also why a silently-broken provider can go unnoticed — see monitoring.

Deprecated hosts

For context when reading old scripts or logs:

HostWas
Azure 20.198.176.70Old backend, scripts/deploy.sh
Kamatera 194.113.195.167 (bz-backend-us)Previous backend, scripts/deploy.kamatera.sh

Both are superseded by scripts/deploy.gcp.sh.

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