BharathTrucks — how it works

An interactive map of the freight platform: the architecture, the flows, the money, the deployment, and the code. Click tabs. Hover nodes for detail. Watch packets flow. Built from the live repo + Coolify deploy on srv2137043.

freight-mcp / matching platform (Next.js) freight-sweep / outbox goclaw desk (WhatsApp/Telegram) database

What BharathTrucks is

One platform for a road-freight brokerage run by one person. A board of loads and trucks, a book of who owes whom, and a desk that answers on WhatsApp/Telegram. One Postgres database holds the business. Everything — browser, desk, outbox — goes through the same API and the same matching rules.

Board

Loads & trucks posted, matched, bid on, tracked through trips, paperwork (LR/POD/e-way), khata, hires, invoices, commissions.

Book

Integer paise ledger (khata_entries.amount_paise). Whole rupees for postings/rates. The two never share a column.

Desk

AI bot on WhatsApp + Telegram posts loads/trucks, negotiates rates (flat ₹150/side = ₹300/load), charges commission. Never invents a rate.

Engine

freight-mcp: 50 MCP tools — city/equipment normalisation, inclusion rules, score. Same engine for browser and phone.

System at a glance

Shipper / Driverbrowser · phone WhatsApp / TGGoClaw desk Traefik:80 / :443 PlatformNext.js 16 · :3000 freight-mcpNode 22 · :8090 freight-sweepNode 22 · :8090 ai-routerNode 22 · :8091 Postgres freightpgvector/pg18 GoClaw:18790

One database for the business. The platform, WhatsApp desk, Telegram desk and outbox all read/write the same freight Postgres. GoClaw keeps its own goclaw DB for agent runtime.

Key facts

Stack
Next.js 16.4 · React 19 · TS 5 · Node 22 · Postgres 18 (pgvector)
Matching
freight-mcp, 50 MCP tools, 24 SQL migrations
UI routes
30+ (board, loads, trucks, matches, bids, trips, fleet, khata, hires, commissions, docs, invoices, assistant, channels, settings, admin)
Tests
40 test files (platform/lib *.test.ts, mcp tools.test.js, ai-router)
Money rule
Postings/rates = whole ₹. Book = integer paise. Never mixed.
Commission
Flat ₹150/side = ₹300 per load (read from config, never hand-computed)
Negotiation
≤3 rounds; >15% gap after round 2 → escalate to ops
Deploy
Coolify-managed Docker Compose on one VPS (189.72.146.117)

Container architecture (C4 level 2)

Major applications, services, APIs and data stores, and how they call each other.

Browser / PWAadmin, office WhatsApp / TGdriver, shipper Traefik v3.7:80 / :443 PlatformNext.js 16 · :3000 ai-routerNode 22 · :8091 GoClaw:18790 freight-mcpNode 22 · :8090 freight-sweepNode 22 · :8090 whisperASR · :9000 Postgres freightpgvector/pg18 Postgres goclawpgvector/pg18 Paperclip:3100 Forgejo:3000

Traefik routes by Host header. Coolify manages lifecycle. GoClaw has its own DB (goclaw). Platform shares freight DB with mcp/sweep.

Component map (one container deep)

platform/ (Next.js)

  • app/(platform)/* 30+ routes
  • lib/*.ts 70+ modules (money, audit, matches, auth, desk, khata, board)
  • lib/*.test.ts 13+ unit tests
  • scripts/migrate.mjs applies 14 SQL migrations

freight-mcp/

  • src/tools.js 196K — 50 MCP tools
  • src/server.js Streamable HTTP /mcp
  • db/migrations/ 24 SQL files (001-024)
  • src/migrate.js auto-applies on boot

freight-sweep/

  • sweep.js delivers queued notifications
  • queue-test.mjs, send-test.mjs for e2e checks
  • Depends: @modelcontextprotocol/sdk, ws

ai-router/

  • src/index.js guard/extract/intents/templates
  • eval/gold.jsonl + 4 tests
  • Stands between console and model

desk/ (bharathtrucks-desk)

  • bridge/bridge.py MCP stdio→HTTP (SSRF bypass)
  • workspace/AGENTS.md desk rules
  • workspace-india/watch/watch.py lane watcher (no-AI)
  • skills/ freight-match, india-forwarding

infra/

  • docker-compose.yml portable stack
  • docker-compose.host.yml Coolify overlay
  • goclaw/coolify-compose.yml gateway stack
  • traefik/*.yml proxy rules

Load → cash (sequence)

The life of a load from a shipper's message to commission collected.

Shipper (TG) GoClaw desk freight-mcp Postgres Driver (TG) "need truck Madurai→Coimbatore" get_profile + save_profile post_load (after confirm) INSERT posts (load) load_id + matches queue_notification (via sweep) "I have truck, ₹X" bid (offer) INSERT offers "driver bids ₹X, accept?" "yes" accept_offer trip created, commission queued "booked. ₹300 commission"

Negotiation ≤3 rounds. After 3 rounds or >15% gap after round 2 → escalate to ops. Commission flat ₹150/side read from get_config.

The money rule

Postings and rates are whole rupees (posts.rate_amount, commissions.amount). The book is integer paise (bids.amount_paise, trips.freight_paise, hires.billed_paise, khata_entries.amount_paise). The two never meet in one column.

Whole rupees (₹)

  • posts.rate_amount — posted rate
  • commissions.amount — agency earning
  • Shown to users, negotiated by desk

Integer paise

  • bids.amount_paise
  • trips.freight_paise
  • hires.billed_paise
  • khata_entries.amount_paise

Khata (the book)

Who owes whom. Every trip creates khata entries. Statements per counterparty, printable + shareable. Invoices: draft → sent → approved → paid. Commissions tracked separately until money + UTR in hand.

Partiesshipper/trucker Tripsfreight_paise Khataamount_paise Invoicesdraft→paid Commissions₹150/side Statementsper counterparty Share links/s /d tokens

Deployment topology (Coolify)

Four Coolify services on one VPS 189.72.146.117. Traefik owns :80/:443, issues Let's Encrypt certs by Host header.

Traefik v3.7:80 :443 bharathtruckslepy... (5 containers) goclawqg16... (patched) papercliphzva... (2 containers) forgejokyjd... (git) bharathtruck.tech desk.bharathtruck.tech paperclip.bharathtruck.tech forgejo...sslip.io platform :3000 freight-mcp :8090 freight-sweep :8090 ai-router :8091 postgres :5432 goclaw :18790 postgres :5432 paperclip :3100 postgres :5432 forgejo :3000

Coolify renames containers *-lepy.... Volumes named lepy..._postgres-data. Shared net freight (legacy host compose) vs isolated Coolify nets.

Repo map (11 repos in Forgejo)

All private, owner iamcoolvivek007, migrated from srv2043063. Total 1.2 GB (images dominate).

Channels — how the desk talks

WhatsApp + Telegram via GoClaw. The desk reads/writes the freight DB through freight-mcp (MCP tools). Outbox (freight-sweep) delivers queued messages over GoClaw WebSocket.

WhatsApp

  • Native via whatsmeow (patched client — published gets 405)
  • Voice notes → local ASR (faster-whisper, :9000)
  • 2 lines: published (+91 88480 01730) + forwarding (+91 85907 26922)

Telegram

  • Bot API, buttons patched (telegram-buttons.patch)
  • Button screens + join links
  • Instance name bharath-trucks-telegram

Bridge

  • bridge.py — MCP stdio→HTTP (SSRF bypass)
  • GoClaw spawns it; dials freight-mcp over freight net
  • Retries once on failure, always answers

Lane watcher

  • watch.py polls engine, no-AI, queues alerts only on new matches
  • State in watches.json
  • Replaced 155 AI runs / 3.6M tokens with polling
MD echo "written"; wc -l /tmp/opencode/explainer/index.html; wc -c /tmp/opencode/explainer/index.html