Files
nick-doc/10 - Services/backend.md
Siavash Sameni e52ffce48a docs: sync vault with codebase state (2026-06-12)
- 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>
2026-06-12 11:42:18 +04:00

25 KiB
Raw Blame History

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 (00000018+)
│   │   └── 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/ (00000018+), 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. typechecknpm ci (cached at /opt/woodpecker-cache/backend-npm) then npm run typecheck; push is blocked if tsc errors exist
  3. build-and-deploydocker 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. notifynode 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

# 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 300800ms on external routes = WAN RTT (~235ms); server-side is 312ms; 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