# 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:` — personal notifications, payment status updates - `payment:` — scoped payment lifecycle events (added in v2.8.4 to prevent global cart-wipe) - `dispute:` — dispute chat and status - `chat:` — 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-` 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-` 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` |