- Update backend, frontend, scanner, deployment, amanat-assist service docs - Update System Overview, Scanner Architecture, Telegram Mini App flow - Update 10 - Services/README.md - Add Tenant data model, Tenant API reference, Tenant Storefront Flow - Add Multi-Shop Branch Project Scan (2026-06-10) - Add tenant.md service doc - Append activity log entry - Reflects archived/search/stats route fix and new E2E test suite Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
426 lines
25 KiB
Markdown
426 lines
25 KiB
Markdown
# Backend Service — amn-backend
|
||
|
||
## 1. Overview
|
||
|
||
`amn-backend` is the Express 5 / TypeScript API server that powers the Amanat escrow marketplace. It is the single authoritative backend for the dev.amn.gg (escrow-dev) and multi.amn.gg (escrow-multi) stacks.
|
||
|
||
| Field | Value |
|
||
|---|---|
|
||
| Current version | **2.11.43** |
|
||
| Status | Production — receiving active feature development |
|
||
| Runtime | Node ≥ 22 |
|
||
| Framework | Express 5 (TypeScript) |
|
||
| Primary DB | PostgreSQL via Drizzle ORM |
|
||
| Mongo status | **Removed** — Mongoose was fully stripped; PostgreSQL is the sole persistence layer |
|
||
| Repo | `git@git.tbs.amn.gg:escrow/backend.git` |
|
||
| Dev stack host | `root@89.58.32.32` — Arcane project `escrow-dev` |
|
||
|
||
---
|
||
|
||
## 2. Tech Stack
|
||
|
||
| Layer | Technology | Notes |
|
||
|---|---|---|
|
||
| HTTP framework | Express 5 | Async error propagation built in |
|
||
| Language | TypeScript (strict) | tsc gate on every CI push |
|
||
| Runtime | Node ≥ 22 | Also used for CI typecheck step |
|
||
| Database | PostgreSQL 15 via Drizzle ORM (`drizzle-orm ^0.45.2`, `pg ^8.21.0`) | Single source of truth; 19+ migrations landed |
|
||
| Auth | JWT (access + refresh) + WebAuthn/Passkey + Google OAuth | `JWT_SECRET`, `REFRESH_TOKEN_EXPIRES_IN` |
|
||
| Session / Mini App | Telegram Mini App `initData` verification | `TELEGRAM_WEBAPP_URL` |
|
||
| Realtime | Socket.IO with Redis adapter (`@socket.io/redis-adapter`) | Room-scoped events |
|
||
| Cache / Pub-Sub | Redis | `REDIS_URI` |
|
||
| Rate limiting | express-rate-limit (in-memory; Redis adapter planned) | Auth 10/15 min, payment 30/15 min, AI 20/15 min, global 100/15 min |
|
||
| Security headers | Helmet | CSP, X-Frame-Options, etc. |
|
||
| File uploads | Multer | MIME validation, `UPLOAD_PATH` |
|
||
| Email | Nodemailer (SMTP) + Resend | `SMTP_*` / `RESEND_API_KEY` |
|
||
| Price oracle | Chainlink + OffchainFX | Depeg protection, `ORACLE_MAX_STALENESS_S` |
|
||
| AML | Chainalysis + OFAC SDN | `CHAINALYSIS_API_KEY`, `OFAC_SDN_URL` |
|
||
| AI | OpenAI | Descriptions, moderation |
|
||
| CI | Woodpecker CI | `.woodpecker/*.yml` |
|
||
| Process model | Node cluster | `CLUSTER_WORKERS` workers + master |
|
||
|
||
---
|
||
|
||
## 3. Directory Structure
|
||
|
||
```
|
||
backend/
|
||
├── src/
|
||
│ ├── app.ts # Express bootstrap: middleware chain, route registration, server creation
|
||
│ ├── cluster.ts # Node cluster master — forks CLUSTER_WORKERS child processes
|
||
│ ├── controllers/ # Thin HTTP handlers that delegate to services
|
||
│ ├── db/ # Drizzle/Postgres layer
|
||
│ │ ├── schema/ # Per-table Drizzle schema files + index.ts barrel
|
||
│ │ ├── migrations/ # Numbered SQL migration files (0000–0018+)
|
||
│ │ └── repositories/ # DrizzleXxxRepo classes + factory.ts
|
||
│ ├── infrastructure/
|
||
│ │ └── socket/ # Socket.IO server init, room helpers, emit wrappers
|
||
│ ├── models/ # Legacy placeholder (Mongoose removed; schemas now in db/schema/)
|
||
│ ├── routes/ # Standalone Express Router files (dispute, blog, points, amn-scanner webhook)
|
||
│ ├── scripts/ # CLI utilities — seed:users, seed:categories, tg-notify.cjs (CI)
|
||
│ ├── seeds/ # Fixture data for local dev (Postgres-capable, idempotent)
|
||
│ ├── services/ # Domain service modules (see §4)
|
||
│ ├── shared/
|
||
│ │ ├── config/index.ts # Typed env-var loader — single import for all config
|
||
│ │ ├── middleware/ # authMiddleware, roleGuard, errorHandler, validators
|
||
│ │ ├── types/ # Cross-cutting TypeScript types and enums
|
||
│ │ └── utils/response-handler.ts # Standard success/error envelope
|
||
│ └── utils/ # Pure utility functions: logger, currencyUtils, etc.
|
||
├── .woodpecker/ # CI pipeline definitions (cleanup, development, manual, production)
|
||
├── Dockerfile.prod # Multi-stage production image
|
||
└── drizzle.config.ts # Drizzle Kit configuration
|
||
```
|
||
|
||
---
|
||
|
||
## 4. Key Services / Modules
|
||
|
||
| Service path | Description |
|
||
|---|---|
|
||
| `services/auth/` | JWT issue/refresh, Google OAuth, WebAuthn/Passkey registration and assertion, password reset |
|
||
| `services/user/` | User profile CRUD, preferences, address book |
|
||
| `services/marketplace/` | PurchaseRequest, SellerOffer, RequestTemplate, ShopSettings — core escrow marketplace |
|
||
| `services/payment/` | Payment orchestration: provider adapters, internal ledger (available/held/releasable), reconciliation, safety confirmations |
|
||
| `services/payment/adapters/` | Provider-neutral adapter interface + registry; plugs in DePay, SHKeeper, amn.scanner, Request Network |
|
||
| `services/payment/requestNetwork/` | Request Network pay-in creation, in-house checkout rehydration, HMAC-verified webhook |
|
||
| `services/payment/wallets/` | HD-derived destination addresses, sweep orchestration, gas top-up |
|
||
| `services/payment/ledger/` | Funds ledger tracking available / held / releasable balances per payment |
|
||
| `services/payment/safety/` | Transaction Safety Provider: AML screening, min-confirmation thresholds |
|
||
| `services/blockchain/` | Web3 read helpers: balance checks, tx verification across ETH / BSC / Base / TON |
|
||
| `services/chat/` | Conversations, messages, attachments |
|
||
| `services/dispute/` | Dispute lifecycle: open, evidence upload, mediator assignment, release-hold |
|
||
| `services/notification/` | Template-based notification delivery (in-app + Telegram); mark-as-read |
|
||
| `services/telegram/` | Bot webhook handler, Mini App `initData` verification, identity link/unlink, seller notifications |
|
||
| `services/points/` | Loyalty points accrual, levels, referrals, redemption |
|
||
| `services/blog/` | Blog posts, categories, comments (public read / admin write) |
|
||
| `services/digital-goods/` | Encrypted digital-goods delivery; key stored under `DIGITAL_GOODS_ENC_KEY` |
|
||
| `services/file/` | Multer multipart upload, MIME validation, static serving under `/uploads` |
|
||
| `services/email/` | Nodemailer SMTP + Resend transport, templated emails |
|
||
| `services/ai/` | OpenAI-backed request description generation and content moderation |
|
||
| `services/redis/` | Redis client singleton, cache helpers, pub-sub wrappers |
|
||
| `services/admin/` | Admin-only endpoints: data cleanup (provider-scoped), confirmation thresholds, awaiting-confirmation view |
|
||
| `services/collection/` | Collection management (multi-seller feature) |
|
||
| `services/delivery/` | Delivery tracking and status |
|
||
| `infrastructure/socket/` | Socket.IO server attached to HTTP server; Redis adapter for multi-process pub-sub |
|
||
|
||
---
|
||
|
||
## 5. API Surface Summary
|
||
|
||
All API routes are mounted under `/api/`. The table below lists top-level route groups.
|
||
|
||
| Mount path | Service module | Auth | Purpose |
|
||
|---|---|---|---|
|
||
| `/api/auth` | `services/auth/authRoutes.ts` | mixed | Login, register, refresh, OAuth, passkey |
|
||
| `/api/user` / `/api/users` | `services/user/userRoutes.ts` | JWT | Profile, preferences |
|
||
| `/api/address` | `services/user/addressRoutes.ts` | JWT | Address CRUD |
|
||
| `/api/marketplace/requests` | `services/marketplace/` | JWT | PurchaseRequest CRUD |
|
||
| `/api/marketplace/offers` | `services/marketplace/` | JWT (seller) | SellerOffer CRUD |
|
||
| `/api/marketplace/templates` | `services/marketplace/` | JWT (seller) | RequestTemplate CRUD |
|
||
| `/api/marketplace/categories` | `services/marketplace/` | public read | Category list |
|
||
| `/api/marketplace/shop-settings` | `services/marketplace/shopSettingsController.ts` | JWT (seller) | Shop profile |
|
||
| `/api/payment` | `services/payment/paymentControllerRoutes.ts` | JWT | Payment CRUD, status, export |
|
||
| `/api/payment/decentralized` | `services/payment/decentralizedPaymentRoutes.ts` | mixed | Legacy/manual Web3 save and verify |
|
||
| `/api/payment/request-network` | `services/payment/requestNetwork/` | mixed + HMAC | RN pay-in, checkout rehydrate, webhook |
|
||
| `/api/payment/derived-destinations` | `services/payment/wallets/` | JWT (admin) | HD address list, sweeps, cron config |
|
||
| `/api/telegram` | `services/telegram/telegramRoutes.ts` | mixed | Mini App session, bot webhook, identity link |
|
||
| `/api/chat` | `services/chat/chatRoutes.ts` | JWT | Conversations, messages |
|
||
| `/api/notification` | `services/notification/` | JWT | List, mark-as-read |
|
||
| `/api/disputes` | `services/dispute/` | JWT | Dispute CRUD, evidence, release-hold |
|
||
| `/api/blog` | `services/blog/blogRoutes.ts` | mixed | Public reads, admin writes |
|
||
| `/api/points` | `services/points/pointsRoutes.ts` | JWT | Points, levels, referrals |
|
||
| `/api/ai` | `services/ai/aiRoutes.ts` | JWT | OpenAI helpers |
|
||
| `/api/files` | `services/file/fileRoutes.ts` | JWT | Multipart upload |
|
||
| `/api/email` | `services/email/emailRoutes.ts` | JWT | Email dispatch |
|
||
| `/api/trezor` | `services/trezor/trezorRoutes.ts` | JWT | Trezor hardware-wallet ops |
|
||
| `/api/admin/cleanup` | `services/admin/dataCleanupRoutes.ts` | JWT (admin) | Data cleanup (must be provider-scoped) |
|
||
| `/api/admin/rn/networks` | `services/payment/requestNetwork/networkRegistryRoutes.ts` | JWT (admin) | RN chain/token registry |
|
||
| `/api/admin/settings/confirmation-thresholds` | `services/admin/confirmationThresholdRoutes.ts` | JWT (admin) | Runtime confirmation thresholds |
|
||
| `/health` | `app.ts` | public | Docker healthcheck; surfaces active Postgres store modes |
|
||
|
||
Full per-endpoint details: [[03 - API Reference/API Overview]]
|
||
|
||
---
|
||
|
||
## 6. Database
|
||
|
||
### PostgreSQL (primary)
|
||
|
||
- Driver: `pg ^8.21.0` via Drizzle ORM (`drizzle-orm ^0.45.2`)
|
||
- Connection: `PG_URL` (primary pool); `PG_VITAL_URL` / `PG_NONVITAL_URL` for split-pool configuration
|
||
- Pool tuning: `PG_POOL_MAX`, `PG_POOL_SIZE`, `PG_NONVITAL_POOL_MAX`
|
||
- Migrations: numbered SQL files in `src/db/migrations/` (0000–0018+), applied via Drizzle Kit (`npx drizzle-kit migrate`)
|
||
- Repositories: `DrizzleXxxRepo` classes in `src/db/repositories/`; factory pattern via `factory.ts`
|
||
- Seeds: idempotent Postgres-capable seed scripts under `src/seeds/`; auto-run on start when `AUTO_SEED_ON_START=true`
|
||
|
||
### MongoDB (retired)
|
||
|
||
MongoDB and Mongoose have been **fully removed** from the runtime. The `MONGODB_URI` and `MIGRATION_MONGO_URL` env vars exist only for optional data backfill tooling in `src/db/repositories/migration/`. No Mongo connection is established at server boot. `MONGO_CONNECT_MODE=never` is the effective runtime mode.
|
||
|
||
The `DATABASE_URL` / `POSTGRES_URL` aliases are accepted for compatibility; prefer `PG_URL`.
|
||
|
||
---
|
||
|
||
## 7. Auth Model
|
||
|
||
### JWT
|
||
|
||
- Access tokens signed with `JWT_SECRET`; expiry controlled by `JWT_EXPIRES_IN`
|
||
- Refresh tokens with `REFRESH_TOKEN_EXPIRES_IN`; stored and rotated server-side
|
||
- `authMiddleware` in `shared/middleware/` verifies tokens and attaches `req.user`
|
||
- Role-based access via `roleGuard('admin' | 'seller' | 'buyer' | 'resolver' | 'guard')`
|
||
|
||
### WebAuthn / Passkey
|
||
|
||
- Passkey registration and assertion handled in `services/auth/`
|
||
- Enables passwordless login on supported clients
|
||
|
||
### Google OAuth
|
||
|
||
- `GOOGLE_CLIENT_ID` enables Google OAuth 2.0 sign-in
|
||
|
||
### Telegram Mini App
|
||
|
||
- Mini App sessions verified via Telegram `initData` HMAC in `services/telegram/`
|
||
- Identity linking ties a Telegram user to a platform account
|
||
- `TELEGRAM_WEBAPP_URL` controls the allowed Mini App origin
|
||
|
||
### Rate limits on auth endpoints
|
||
|
||
- Login: 10 requests per 15-minute window (`LOGIN_RATE_LIMIT_ENABLED` to toggle)
|
||
- Cloudflare Turnstile CAPTCHA support: `TURNSTILE_SECRET_KEY`
|
||
|
||
---
|
||
|
||
## 8. Realtime (Socket.IO)
|
||
|
||
- Socket.IO server is attached to the HTTP server at bootstrap (`infrastructure/socket/socketService.ts`)
|
||
- Redis adapter (`@socket.io/redis-adapter`) enables pub-sub across Node cluster workers
|
||
- **Room conventions:**
|
||
- `user:<userId>` — personal notifications, payment status updates
|
||
- `payment:<paymentId>` — scoped payment lifecycle events (added in v2.8.4 to prevent global cart-wipe)
|
||
- `dispute:<disputeId>` — dispute chat and status
|
||
- `chat:<conversationId>` — chat messages
|
||
- **Key emitted events:** `payment:update`, `notification:new`, `dispute:update`, `chat:message`, `offer:update`
|
||
- Server verifies JWT on `connection` and room join; frontend must join the correct room after authenticating
|
||
|
||
---
|
||
|
||
## 9. Payment Providers
|
||
|
||
| Provider | Type | Chains / Tokens | Notes |
|
||
|---|---|---|---|
|
||
| **amn.scanner** | In-house on-chain scanner | ETH, BSC (USDT/USDC) | Bearer auth via `AMN_SCANNER_API_KEY`; webhook secret `AMN_SCANNER_WEBHOOK_SECRET`; provider tag `"amn.scanner"` |
|
||
| **Request Network** | Decentralized invoicing | ETH, Base (USDC/DAI) | `REQUEST_NETWORK_*` env block; HMAC webhook signature; canonical proxy addresses differ per chain (ETH `0x370DE2…`, Base `0x189219…`) |
|
||
| **SHKeeper** | Self-hosted crypto gateway | BTC, ETH, BNB, USDT, others | `SHKEEPER_NETWORK`, `SHKEEPER_NETWORKS`, `SHKEEPER_ALLOWED_TOKENS` |
|
||
| **DePay** | Web3 payment widget | EVM chains | Legacy path; `PAYMENT_CALLBACK_SECRET` |
|
||
| **Derived Destinations** | HD-wallet receive addresses | ETH / BSC | `DERIVED_DESTINATION_XPUB/XPRIV`; sweep orchestration runs on configurable interval |
|
||
|
||
### Payment orchestration
|
||
|
||
- `PAYMENT_PROVIDER_MODE` selects active provider(s) at runtime
|
||
- Internal ledger tracks `available`, `held`, and `releasable` balances per payment record
|
||
- Transaction Safety Provider: AML screening (Chainalysis / OFAC SDN), minimum on-chain confirmation thresholds configurable at runtime (`TRANSACTION_SAFETY_MIN_CONFIRMATIONS`, `TRANSACTION_SAFETY_AML_PROVIDER`)
|
||
- `GET /api/payment/:id` is exempt from the payment rate limiter (polling-safe)
|
||
- Cleanup endpoints must always be scoped by `provider:` to avoid wiping unrelated payment records
|
||
|
||
### Price Oracle
|
||
|
||
- Chainlink + OffchainFX feeds; `ORACLE_MAX_STALENESS_S` sets maximum acceptable quote age
|
||
- Depeg protection rejects or flags stablecoin payments when peg deviation exceeds threshold
|
||
- `ORACLE_BYPASS_ENABLED=true` disables staleness check (dev/test only)
|
||
|
||
---
|
||
|
||
## 10. CI/CD (Woodpecker)
|
||
|
||
Four Woodpecker pipeline files under `.woodpecker/`:
|
||
|
||
| File | Trigger | Purpose |
|
||
|---|---|---|
|
||
| `production.yml` | push to `main` / `master` | Typecheck → build Docker image locally on host → `docker compose up -d backend` |
|
||
| `development.yml` | cron (parked) | Was the dev-stack auto-deploy; currently inactive |
|
||
| `manual.yml` | manual trigger | Builds image to `git.tbs.amn.gg` registry (escrow-dev stack ignores registry pulls) |
|
||
| `cleanup.yml` | scheduled | Housekeeping tasks (prune old images, stale data) |
|
||
|
||
### Production pipeline steps
|
||
|
||
1. **get-version** — reads `package.json` version, writes `dev-<version>` to `.tags`
|
||
2. **typecheck** — `npm ci` (cached at `/opt/woodpecker-cache/backend-npm`) then `npm run typecheck`; push is blocked if tsc errors exist
|
||
3. **build-and-deploy** — `docker build -f Dockerfile.prod -t escrow-backend-local:dev .` on the agent co-located with the stack; then `docker compose up -d --no-deps --pull never backend`
|
||
4. **notify** — `node scripts/ci/tg-notify.cjs` posts success/failure to Telegram (no `parse_mode` to avoid HTML/Markdown breakage)
|
||
|
||
### escrow-multi stack
|
||
|
||
The `escrow-multi` stack (branch `feature/white-label-shops`) uses `.woodpecker/multi.yml`. Always deploy via `git push forgejo feature/white-label-shops` — never via manual SSH or rsync. Woodpecker CLI credentials are in `~/CascadeProjects/escrow/.env`.
|
||
|
||
### Version bump requirement
|
||
|
||
Bump `package.json` version before every CI-triggering push, or the deployed image will not be distinguishable from the previous build. See memory note `version_bump_before_ci.md`.
|
||
|
||
---
|
||
|
||
## 11. Local Development Quick-Start
|
||
|
||
```bash
|
||
# 1. Clone
|
||
git clone git@git.tbs.amn.gg:escrow/backend.git
|
||
cd backend
|
||
|
||
# 2. Install dependencies
|
||
npm install
|
||
|
||
# 3. Copy and populate env
|
||
cp .env.example .env.development
|
||
# Edit .env.development — minimum required: PG_URL, REDIS_URI, JWT_SECRET, FRONTEND_URL
|
||
|
||
# 4. Start Postgres and Redis (Docker)
|
||
docker compose up -d postgres redis
|
||
|
||
# 5. Run migrations
|
||
npx drizzle-kit migrate
|
||
|
||
# 6. Start dev server (seeds run automatically if SEED_USERS=true)
|
||
npm run dev
|
||
# Server starts on process.env.PORT
|
||
|
||
# 7. Type-check only (no run)
|
||
npm run typecheck
|
||
```
|
||
|
||
> The pre-push git hook runs a full `tsc` check. If a parallel agent's mid-refactor tree is checked out, this hook may block your push. Stage only your specific files — never `git add -A` blindly. See memory note `backend_prepush_tsc_hook.md`.
|
||
|
||
---
|
||
|
||
## 12. Environment Variables
|
||
|
||
| Variable | Description |
|
||
|---|---|
|
||
| `PORT` | HTTP listen port |
|
||
| `NODE_ENV` | `development` / `production` / `test` |
|
||
| `FRONTEND_URL` | Allowed CORS origin (frontend base URL) |
|
||
| `BACKEND_URL` | Self-referential base URL (used for webhook callback construction) |
|
||
| `PG_URL` | Primary Postgres connection string |
|
||
| `PG_VITAL_URL` | Postgres connection for vital (write-path) pool |
|
||
| `PG_NONVITAL_URL` | Postgres connection for non-vital (read-path) pool |
|
||
| `PG_POOL_MAX` | Max connections in primary pool |
|
||
| `PG_POOL_SIZE` | Pool size alias |
|
||
| `PG_NONVITAL_POOL_MAX` | Max connections in non-vital pool |
|
||
| `DATABASE_URL` / `POSTGRES_URL` | Compatibility aliases for `PG_URL` |
|
||
| `REDIS_URI` | Redis connection string (sessions, pub-sub, Socket.IO adapter) |
|
||
| `JWT_SECRET` | HMAC secret for JWT signing |
|
||
| `JWT_EXPIRES_IN` | Access token TTL (e.g. `15m`) |
|
||
| `REFRESH_TOKEN_EXPIRES_IN` | Refresh token TTL (e.g. `7d`) |
|
||
| `GOOGLE_CLIENT_ID` | Google OAuth 2.0 client ID |
|
||
| `TELEGRAM_WEBAPP_URL` | Allowed Telegram Mini App origin |
|
||
| `TG_NOTIFY_BOT_TOKEN` | Telegram bot token for CI/admin notifications |
|
||
| `TG_NOTIFY_CHATS` | Comma-separated Telegram chat IDs for notifications |
|
||
| `SMTP_HOST` | SMTP server hostname |
|
||
| `SMTP_PORT` | SMTP port |
|
||
| `SMTP_SECURE` | `true` for TLS |
|
||
| `SMTP_USER` | SMTP auth username |
|
||
| `SMTP_PASS` | SMTP auth password |
|
||
| `SMTP_FROM` | From address for outbound email |
|
||
| `RESEND_API_KEY` | Resend email API key |
|
||
| `RESEND_WEBHOOK_SECRET` | Resend webhook signature secret |
|
||
| `PAYMENT_PROVIDER_MODE` | Active payment provider(s) |
|
||
| `PAYMENT_CALLBACK_SECRET` | DePay callback HMAC secret |
|
||
| `AMN_SCANNER_URL` | amn.scanner service base URL |
|
||
| `AMN_SCANNER_API_KEY` | Bearer token for amn.scanner API |
|
||
| `AMN_SCANNER_WEBHOOK_SECRET` | HMAC secret for amn.scanner webhook verification |
|
||
| `REQUEST_NETWORK_API_BASE_URL` | Request Network API base URL |
|
||
| `REQUEST_NETWORK_API_KEY` | Request Network API key |
|
||
| `REQUEST_NETWORK_CLIENT_ID` | RN client identifier |
|
||
| `REQUEST_NETWORK_NETWORK` | RN chain name (`mainnet` / `sepolia` / etc.) |
|
||
| `REQUEST_NETWORK_RECEIVER_ADDRESS` | Merchant wallet for RN payments |
|
||
| `REQUEST_NETWORK_PAYMENT_CURRENCY` | Payment token symbol |
|
||
| `REQUEST_NETWORK_PAYMENT_TOKEN_ADDRESS` | Payment token contract address |
|
||
| `REQUEST_NETWORK_INVOICE_CURRENCY` | Invoice denomination currency |
|
||
| `REQUEST_NETWORK_WEBHOOK_CALLBACK_URL` | RN webhook delivery URL |
|
||
| `REQUEST_NETWORK_WEBHOOK_SECRET` | HMAC secret for RN webhook |
|
||
| `REQUEST_NETWORK_MERCHANT_REFERENCE` | RN merchant reference string |
|
||
| `REQUEST_NETWORK_ORIGIN` | RN request origin header |
|
||
| `RN_API_KEY` | Alias for `REQUEST_NETWORK_API_KEY` |
|
||
| `RN_API_URL` | Alias for `REQUEST_NETWORK_API_BASE_URL` |
|
||
| `RN_CLIENT_ID` | Alias for `REQUEST_NETWORK_CLIENT_ID` |
|
||
| `RN_WEBHOOK_SECRET` | Alias for `REQUEST_NETWORK_WEBHOOK_SECRET` |
|
||
| `SHKEEPER_NETWORK` | SHKeeper primary network identifier |
|
||
| `SHKEEPER_NETWORKS` | SHKeeper supported networks (comma-separated) |
|
||
| `SHKEEPER_ALLOWED_TOKENS` | Token allowlist for SHKeeper |
|
||
| `DERIVED_DESTINATION_XPUB` | HD wallet extended public key for address derivation |
|
||
| `DERIVED_DESTINATION_XPRIV` | HD wallet extended private key (for sweep signing) |
|
||
| `DERIVED_DESTINATION_BASE_PATH` | BIP-44 derivation base path |
|
||
| `DERIVED_DESTINATION_CHAIN_ID` | EVM chain ID for derived address sweeps |
|
||
| `DERIVED_DESTINATION_MIN_SWEEP_AMOUNT` | Minimum balance to trigger a sweep |
|
||
| `DERIVED_DESTINATION_SWEEP_INTERVAL_MS` | Sweep polling interval in milliseconds |
|
||
| `DERIVED_DESTINATION_SWEEP_BALANCE_CONCURRENCY` | Parallel balance-check concurrency |
|
||
| `DERIVED_DESTINATION_SWEEP_SIGNER` | Sweep transaction signing mode |
|
||
| `DERIVED_DESTINATION_SWEEP_AUTOSTART` | Auto-start sweep cron on boot |
|
||
| `SWEEP_MASTER_PRIVKEY` | Master private key for sweep gas top-up |
|
||
| `SWEEP_GAS_MIN_BNB` | Minimum BNB balance before gas top-up is triggered |
|
||
| `SWEEP_GAS_TOP_UP_BNB` | Amount of BNB to top up for sweep gas |
|
||
| `ESCROW_WALLET_ADDRESS` | Platform escrow wallet address |
|
||
| `RECEIVER_WALLET_ADDRESS` | Platform receiver wallet address |
|
||
| `INFURA_KEY` | Infura RPC key (ETH mainnet) |
|
||
| `BSC_RPC_URL` | BSC mainnet RPC endpoint |
|
||
| `BSC_TESTNET_RPC_URL` | BSC testnet RPC endpoint |
|
||
| `BNB_TESTNET_RPC_URL` | BNB testnet RPC endpoint |
|
||
| `RPC_URL_CHAIN_56` | BSC mainnet RPC (chain ID 56) |
|
||
| `RPC_URL_CHAIN_97` | BSC testnet RPC (chain ID 97) |
|
||
| `ENABLE_TESTNET_CHAINS` | Enable testnet chain support |
|
||
| `TRANSACTION_SAFETY_AML_PROVIDER` | AML provider: `chainalysis` / `ofac` / `none` |
|
||
| `TRANSACTION_SAFETY_MIN_CONFIRMATIONS` | Default minimum on-chain confirmations |
|
||
| `CHAINALYSIS_API_KEY` | Chainalysis KYT API key |
|
||
| `OFAC_SDN_URL` | OFAC SDN list endpoint |
|
||
| `AML_CHECK_COST_USD` | Cost per AML check (for billing/reporting) |
|
||
| `ORACLE_MAX_STALENESS_S` | Maximum age (seconds) for oracle price quotes |
|
||
| `ORACLE_BYPASS_ENABLED` | Disable oracle staleness check (`true` in dev/test only) |
|
||
| `DIGITAL_GOODS_ENC_KEY` | AES encryption key for digital goods delivery |
|
||
| `TREZOR_SAFEKEEPING_REQUIRED` | Require Trezor safekeeping confirmation |
|
||
| `TURNSTILE_SECRET_KEY` | Cloudflare Turnstile CAPTCHA secret |
|
||
| `RATE_LIMIT_WINDOW_MS` | Rate limit window in milliseconds |
|
||
| `RATE_LIMIT_MAX_REQUESTS` | Max requests per window (global limiter) |
|
||
| `RATE_LIMIT_BYPASS_IPS` | Comma-separated IPs exempt from rate limiting |
|
||
| `LOGIN_RATE_LIMIT_ENABLED` | Enable/disable login rate limiter |
|
||
| `TRUST_PROXY_HOPS` | `trust proxy` hop count for X-Forwarded-For behind Traefik |
|
||
| `UPLOAD_PATH` | Filesystem path for uploaded files (default `/app/uploads`) |
|
||
| `MAX_FILE_SIZE` | Maximum upload size in bytes |
|
||
| `CLUSTER_WORKERS` | Number of Node cluster worker processes |
|
||
| `SEED_USERS` | Seed default dev users on start |
|
||
| `AUTO_SEED_ON_START` | Auto-run all seeds on process start |
|
||
| `SEED_DIGITAL_GOODS_ON_START` | Seed digital goods fixtures on start |
|
||
| `SEED_MOCK_SHOPS_ON_START` | Seed mock shop fixtures on start |
|
||
| `FORCE_SEED_TEMPLATES` | Force re-seed request templates even if already present |
|
||
| `SEED_PASSWORD_SELLER` | Password for seeded seller account |
|
||
| `SEED_PASSWORD_MOCK_SELLER` | Password for seeded mock seller account |
|
||
| `SEED_PASSWORD_SUPPORT` | Password for seeded support account |
|
||
| `ADMIN_EMAIL` | Seeded admin user email |
|
||
| `ADMIN_PASSWORD` | Seeded admin user password |
|
||
| `ADMIN_FIRST_NAME` | Seeded admin first name |
|
||
| `ADMIN_LAST_NAME` | Seeded admin last name |
|
||
| `MIGRATION_MONGO_URL` | Mongo URL used only by migration/backfill tooling (not runtime) |
|
||
| `MIGRATION_PG_URL` | Postgres URL used by migration tooling (may differ from `PG_URL`) |
|
||
| `MONGODB_URI` | Legacy Mongo URI retained for backfill scripts only |
|
||
| `DB_NAME` | Database name (legacy config field) |
|
||
|
||
> Store-mode env vars (`AUTH_STORE`, `USER_STORE`, `BLOG_STORE`, etc.) were part of the dual-write migration scaffolding. All domains are now Postgres-only; these can be left unset or set to `postgres`.
|
||
|
||
---
|
||
|
||
## 13. Known Issues / Open Items
|
||
|
||
| Issue | Status | Reference |
|
||
|---|---|---|
|
||
| Rate limit counters are in-memory | Not multi-process safe across cluster workers; Redis adapter planned | `backend_rate_limits.md` |
|
||
| `pgId` vs legacy `_id` mismatch | Auth `_id` is a legacy ObjectId; marketplace FKs use Postgres UUID (`pgId`); match offers on `pgId` | `pgid_vs_legacy_id.md` |
|
||
| Socket.IO room scoping for payments | Backend room-scoping for payment events is an open follow-up (frontend gate added in v2.8.4) | `cart_wipe_global_socket_events.md` |
|
||
| Performance is WAN-bound | Profiling shows 300–800ms on external routes = WAN RTT (~235ms); server-side is 3–12ms; PG migration does not fix this | `perf_is_network_bound_not_db.md` |
|
||
| RN webhook `event` field | Request Network sends discriminator as `payload.event` not `eventType`; parser must include `event` in fallback chain | `rn_webhook_event_field.md` |
|
||
| RN canonical proxy addresses per chain | ETH `0x370DE2…`, Base `0x189219…` — not the same CREATE2 address; always probe before using hardcoded addresses | `rn_proxy_addresses_per_chain.md` |
|
||
| JSON assets not copied to dist | `tsc` does not copy `.json` files; any `fs.readFileSync` on JSON needs explicit `postbuild` copy step | `feedback_json_assets_copy_to_dist.md` |
|
||
| Woodpecker `${VAR}` template collision | Woodpecker eats `${VAR}` in commands; use `$VAR` or `$$VAR` | `woodpecker_template_collision.md` |
|
||
| CI silent build fail | Green CI does not guarantee image was pushed to registry; verify `dev-<version>` tag exists before trusting | `woodpecker_silent_build_fail.md` |
|
||
| Admin cleanup must be provider-scoped | Any payment cleanup query must filter by `provider:` or it silently destroys multi-seller/RN records | `feedback_payment_cleanup_provider_filter.md` |
|
||
| Store-mode env vars | Legacy dual-write `*_STORE` vars still present in codebase but are no-ops; can be pruned in a future cleanup | — |
|
||
| Mongo backfill tooling | `MIGRATION_MONGO_URL` / `MONGODB_URI` retained for backfill scripts only; server never connects to Mongo at runtime | `mongo_retirement_status.md` |
|