Files
nick-doc/10 - Services/backend.md
Siavash Sameni 67244223ec docs: add sub-project service docs + sync vault 2026-06-08
Add 10 - Services/ docs for all sub-projects: backend, frontend, scanner,
deployment (new), update amanat-assist. Update Scanner Architecture,
Telegram Mini App flow, and Activity Log. Add payment safety edge cases.
2026-06-08 16:23:00 +04:00

467 lines
24 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Backend Service — amn-backend
## 1. Overview
**amn-backend** is the Express 5 / TypeScript API server powering the Amanat escrow marketplace (`dev.amn.gg`). It handles all buyer-seller escrow workflow logic, crypto payment processing across multiple chains and providers, real-time socket events, authentication, admin tooling, and the in-progress Mongo→PostgreSQL migration.
| Field | Value |
|---|---|
| Current version | **2.10.5** |
| Status | Active — production at `dev.amn.gg` |
| Repo | `git@git.tbs.amn.gg:escrow/backend.git` |
| Runtime port | 8083 (production Docker), 8080 (dev Docker), 5001 (dev default) |
| Database | PostgreSQL (Drizzle ORM) — sole persistence layer as of v2.9.12 |
| Node version | 22 (`.nvmrc`) |
PostgreSQL is the sole active database. MongoDB references remain in some env-var config for the dual-write seam during migration, but no Mongo-backed stores remain active in normal operation (all 11 repository domains use Drizzle repos).
---
## 2. Tech Stack
| Layer | Technology |
|---|---|
| Framework | Express 5 (TypeScript) |
| Runtime | Node.js 22 |
| Primary DB | PostgreSQL via Drizzle ORM (`drizzle-orm ^0.45.2`, `pg ^8.21.0`) |
| Migrations | Drizzle Kit (`drizzle-kit ^0.31.1`) — 19 landed SQL migrations |
| Session / Cache | Redis (`ioredis`) with Socket.IO pub-sub adapter |
| Realtime | Socket.IO with Redis adapter (seller/buyer rooms) |
| Auth | JWT (`jsonwebtoken`), Google OAuth, WebAuthn passkeys (`@simplewebauthn/server`), Telegram Mini App initData |
| Crypto payments | Request Network, amn.scanner (in-house), DePay, SHKeeper |
| Rate limiting | In-memory (express-rate-limit) — Redis adapter planned |
| AI integration | OpenAI (listing descriptions, moderation) |
| Email | Nodemailer via Resend SMTP |
| Telegram | Bot webhook + Mini App session + identity linking |
| Security | Helmet, CORS, Cloudflare Turnstile CAPTCHA, HMAC webhook verification |
| Containerization | Docker (Dockerfile.prod, Dockerfile.dev) |
| CI/CD | Woodpecker CI (4 pipelines) |
---
## 3. Directory Structure
```
backend/src/
├── app.ts # Express bootstrap, middleware chain, route registration, graceful shutdown
├── cluster.ts # Node.js cluster mode entry point (multi-core)
├── controllers/ # HTTP request handlers — thin layer, delegate to services
├── db/ # Drizzle/Postgres layer
│ ├── schema/ # Per-table Drizzle schema files + index.ts barrel
│ ├── migrations/ # 19 numbered SQL migration files (00000018)
│ └── repositories/ # Drizzle repos, factory.ts, backfill scripts, verify utilities
├── infrastructure/
│ └── socket/ # Socket.IO server init, room helpers, emit wrappers
├── models/ # Removed — replaced by Drizzle schemas in db/schema/
├── routes/ # Express Router definitions (mounted in app.ts)
│ ├── amnScannerWebhookRoutes.ts
│ ├── blogRoutes.ts
│ ├── disputeRoutes.ts
│ └── pointsRoutes.ts
├── scripts/ # CLI utilities (seed:users, seed:categories, backfill, etc.)
├── seeds/ # Seed data fixtures (Postgres-capable, store-aware, idempotent)
├── services/ # Feature domain services (self-contained per domain)
│ ├── address/ # Address management
│ ├── admin/ # Admin-only operations, AML config, break-glass, data cleanup
│ ├── ai/ # OpenAI integration (descriptions, moderation)
│ ├── auth/ # JWT, OAuth, Passkey, Telegram, password reset
│ ├── blockchain/ # Web3 read/verify helpers
│ ├── blog/ # Posts, categories, comments
│ ├── chat/ # Conversations, messages, attachments
│ ├── config/ # Runtime config service
│ ├── delivery/ # Delivery tracking
│ ├── dispute/ # Dispute lifecycle, evidence, mediator assignment
│ ├── email/ # Nodemailer transport + templates
│ ├── file/ # Multer uploads, MIME validation
│ ├── health/ # Health check endpoint logic
│ ├── marketplace/ # PurchaseRequest, SellerOffer, Template, Shop
│ ├── notification/ # Templates, delivery, mark-as-read
│ ├── payment/ # Payment orchestration + provider adapters + ledger
│ │ ├── adapters/ # Provider-neutral adapter interface + registry
│ │ ├── amnScanner/ # amn.scanner in-house pay-in detection
│ │ ├── ledger/ # Internal funds ledger (available / held / releasable)
│ │ ├── migration/ # Legacy data backfill utilities
│ │ ├── observability/ # Logging and incident controls
│ │ ├── orchestration/ # High-level payment flow coordination
│ │ ├── priceOracle/ # Chainlink + off-chain FX oracle, depeg protection
│ │ ├── reconciliation/ # Webhook + status reconciliation per provider
│ │ ├── request-network/# Request Network routes and webhook signature
│ │ ├── requestNetwork/ # Request Network service logic
│ │ ├── safety/ # Transaction Safety Provider + confirmation thresholds
│ │ ├── tokens/ # On-chain token registry / decimals lookup
│ │ └── wallets/ # Derived destination wallets + sweep orchestration
│ ├── points/ # Loyalty points, levels, redemption
│ ├── redis/ # Redis client, cache helpers, pub-sub
│ ├── telegram/ # Bot webhook, Mini App session, identity linking, notifications
│ ├── trezor/ # Trezor hardware-wallet signing for admin approvals
│ └── user/ # Profile, preferences, addresses
├── shared/
│ ├── config/index.ts # Centralised typed env-var loader
│ ├── middleware/ # authMiddleware, errorHandler, roleGuard, validators
│ ├── types/ # Cross-cutting TypeScript types
│ └── utils/response-handler.ts # Standard success/error response envelope
└── utils/ # Pure utilities (logger, currencyUtils, etc.)
```
Each service folder is self-contained: `<feature>Service.ts`, `<feature>Controller.ts`, `<feature>Routes.ts`, `<feature>Validation.ts`. This design allows future extraction to microservices with minimal coupling.
---
## 4. Key Services / Modules
| Module | Description |
|---|---|
| `services/auth/` | JWT issuance/refresh, Google OAuth, WebAuthn passkeys, Telegram initData verification, password reset |
| `services/marketplace/` | Core escrow domain: PurchaseRequest, SellerOffer, Template, Shop lifecycle |
| `services/payment/` | Payment orchestration, provider adapters, internal ledger, reconciliation |
| `services/payment/ledger/` | Double-spend guard: tracks available / held / releasable balances per payment |
| `services/payment/wallets/` | Derived destination address derivation (xpub) + sweep orchestration |
| `services/payment/priceOracle/` | Chainlink + off-chain FX oracle for multi-currency pricing + stablecoin depeg protection |
| `services/payment/safety/` | Transaction Safety Provider: confirmation thresholds, tx hash and transfer match enforcement |
| `services/payment/amnScanner/` | In-house blockchain scanner webhook adapter (replaces Request Network for pay-in detection) |
| `services/payment/requestNetwork/` | Request Network pay-in routes, webhook signature verification, invoice creation |
| `infrastructure/socket/` | Socket.IO server init, buyer/seller room management, emit helpers |
| `services/redis/` | Redis client wrapper, pub-sub channel helpers, session cache |
| `services/chat/` | Conversations and message threading between buyer and seller |
| `services/dispute/` | Dispute lifecycle: open, evidence, mediator, resolution |
| `services/admin/` | Admin RBAC operations: AML config, break-glass, dispute management, data cleanup |
| `services/telegram/` | Bot webhook handler, Mini App session auth, Telegram identity linking, push notifications |
| `services/trezor/` | Trezor hardware-wallet approval gate for high-value admin actions (break-glass overrideable) |
| `services/notification/` | In-app notification templates, delivery, mark-as-read |
| `services/ai/` | OpenAI integration: AI-assisted listing descriptions and content moderation |
| `services/email/` | Nodemailer transport via Resend SMTP, HTML email templates |
| `services/points/` | Loyalty points engine, tier levels, redemption |
| `services/blog/` | Blog posts, categories, comments |
| `services/file/` | Multer-based file upload handler, MIME validation, upload path management |
| `services/blockchain/` | Low-level Web3 read helpers: balance checks, tx confirmation polling |
| `db/repositories/` | Drizzle ORM repository layer for all 11 domain entities |
| `seeds/` | Idempotent Postgres seed fixtures for users, categories, shops, configs |
| `scripts/` | CLI backfill, migration verify, seeding, and maintenance scripts |
---
## 5. API Surface Summary
All routes are mounted under `/api/*`. See [[03 - API Reference/API Overview]] for the full endpoint reference.
Key route groups:
| Prefix | Domain |
|---|---|
| `/api/auth/*` | Registration, login, OAuth, passkeys, Telegram auth |
| `/api/payment/*` | Payment CRUD, status polling, provider webhooks |
| `/api/payment/request-network/*` | Request Network webhook + invoice endpoints |
| `/api/amn-scanner/*` | amn.scanner webhook receiver |
| `/api/marketplace/*` | Purchase requests, seller offers, templates |
| `/api/chat/*` | Conversations, messages, attachments |
| `/api/dispute/*` | Dispute lifecycle |
| `/api/admin/*` | Admin operations (role-gated) |
| `/api/notification/*` | In-app notifications |
| `/api/blog/*` | Blog posts and comments |
| `/api/points/*` | Loyalty points |
| `/api/user/*` | User profile, preferences |
| `/health` | Docker healthcheck + active store mode listing |
**Rate limits (active):**
| Scope | Limit |
|---|---|
| Auth endpoints | 10 req / 15 min |
| Payment endpoints | 30 req / 15 min |
| AI endpoints | 20 req / 15 min |
| Global | 100 req / 15 min |
| `GET /api/payment/:id` | Exempt (polling route) |
| RN + Telegram webhooks | Exempt from global limiter |
---
## 6. Database
### PostgreSQL (primary — active)
- **ORM:** Drizzle ORM (`drizzle-orm ^0.45.2`)
- **Driver:** `pg ^8.21.0`
- **Migrations:** 19 SQL files under `src/db/migrations/` (00000018), managed by `drizzle-kit`
- **Schemas:** per-table files in `src/db/schema/`, exported via `index.ts` barrel
- **Repositories:** `src/db/repositories/` — one Drizzle repo per domain; `factory.ts` provides DI
- **Connection:** `PG_URL` env var (`postgres://user:pass@host:5432/db`)
- **Migrations run:** `npx drizzle-kit migrate` (or via `drizzle.config.ts`)
### MongoDB (legacy — migration in progress)
MongoDB and Mongoose were removed at the code level as of v2.9.12. The `MONGO_CONNECT_MODE` env var and `*_STORE` vars remain for the dual-write seam but all active domain stores use Drizzle exclusively. Remaining migration work:
- Backfill execution for remaining legacy records
- Per-domain read cutover verification
- Chat domain normalization (current blocker)
- Full runtime coupling severance
See [[PRD - Mongo Retirement (Full Nuke).md]] and [[MIGRATION_TODO.md]] for status.
---
## 7. Auth Model
| Method | Mechanism |
|---|---|
| Password | bcrypt hashed, JWT access + refresh token pair |
| Google OAuth | OAuth 2.0 code flow via `google-auth-library` |
| WebAuthn / Passkeys | `@simplewebauthn/server` — RP ID: `dev.amn.gg` |
| Telegram Mini App | initData HMAC verification (bot token), replay window: 120 s, TTL: 24 h |
| Telegram Bot | Webhook secret token header verification |
| Sessions | Stateless JWT; refresh token stored in Redis |
| CAPTCHA | Cloudflare Turnstile, triggered after 3 failed login attempts from same IP |
**RBAC roles:** `admin`, `buyer`, `seller`, `resolver`, `guard`
`roleGuard(role)` middleware is applied per-route after `authMiddleware`. The admin role unlocks break-glass, AML config, dispute management, and data cleanup endpoints.
**Trezor safekeeping:** when `TREZOR_SAFEKEEPING_REQUIRED=true`, high-value admin actions (release, refund, payout) require a Trezor-signed approval message. Break-glass overrides this for 1 hour and fires a Telegram alarm.
---
## 8. Realtime (Socket.IO)
- **Adapter:** Redis pub-sub (`@socket.io/redis-adapter`) — scales across multiple backend instances
- **Init:** `infrastructure/socket/socketService.ts` — attaches to the HTTP server after Express bootstraps
- **Room model:**
- `buyer:<userId>` — buyer-facing events (payment status, offer updates, cart)
- `seller:<userId>` — seller-facing events (new requests, offer accepted)
- Admin rooms for dispute/notification broadcasts
- **Auth:** Socket handshake verified with JWT before room join
- **Known issue:** Global payment broadcasts previously wiped all users' carts (fixed in frontend v2.8.4 with a provider gate). Backend room-scoping is an open follow-up item.
Key emitted events (non-exhaustive):
| Event | Direction | Description |
|---|---|---|
| `payment:status` | Server → client | Payment state change (pending → confirmed → released) |
| `offer:new` | Server → seller | New purchase request from buyer |
| `offer:accepted` | Server → buyer | Seller accepted the offer |
| `notification:new` | Server → client | In-app notification delivery |
| `dispute:update` | Server → both | Dispute state change |
| `chat:message` | Server → both | New chat message in conversation |
---
## 9. Payment Providers
The payment layer uses a provider-neutral adapter interface (`services/payment/adapters/`). All providers register in the adapter registry. The ledger (`services/payment/ledger/`) enforces double-spend prevention across all providers.
| Provider | Type | Chains | Status |
|---|---|---|---|
| **amn.scanner** | In-house blockchain scanner | ETH, BSC, Base, TON | Active — default for new payments when `AMN_SCANNER_DEFAULT=true` |
| **Request Network** | Decentralized payment protocol | BSC (USDC/USDT) + ETH | Active — legacy in-flight payments; webhook-driven |
| **DePay** | Widget-based crypto payments | Multi-chain | Available via adapter |
| **SHKeeper** | Self-hosted crypto gateway | Bitcoin + EVM | Available via adapter |
### Payment flow
1. Buyer creates intent (`POST /api/payment`) → provider adapter creates invoice / watch address
2. Provider webhook arrives → HMAC-verified → reconciliation service updates ledger
3. Escrow holds funds → seller fulfills → admin/resolver releases or refunds
4. Ledger enforces: held → releasable → released (no double-spend)
### amn.scanner specifics
- Webhook endpoint: `POST /api/amn-scanner/webhook`
- HMAC verification via `AMN_SCANNER_WEBHOOK_SECRET`
- Discriminator field: `payload.event` (not `eventType`) — always check this field
- Provider scoped by `provider: "amn.scanner"` in payment records
- Read token decimals on-chain, not from registry
### Request Network specifics
- Webhook endpoint: `POST /api/payment/request-network/webhook`
- Webhook secret: `REQUEST_NETWORK_WEBHOOK_SECRET`
- Network: BSC mainnet, currency: USDC
- Canonical proxy addresses differ per chain (ETH: `0x370DE2…`, Base: `0x189219…`) — probe before trusting
### Safety layer
- `TRANSACTION_SAFETY_MIN_CONFIRMATIONS=12` (default)
- Requires tx hash match and on-chain transfer match before releasing funds
- AML screening: `none` (default), `ofac` (OFAC SDN list, local, free), or `chainalysis`
### Price oracle / depeg protection
- Providers: Chainlink + off-chain FX (`OFFCHAIN_FX_URL`)
- Chains: ETH (RPC via `CHAINLINK_RPC_1`), BSC (via `CHAINLINK_RPC_56`)
- Depeg hard cap: `DEPEG_HARD_CAP_BPS` (default 500 bps = 5%)
- Oracle max staleness: `ORACLE_MAX_STALENESS_S=120`
- Currently disabled (`ORACLE_QUOTING_ENABLED=false`) — enable after FX feeds are configured
---
## 10. CI/CD (Woodpecker)
Four pipelines in `backend/.woodpecker/`:
### `production.yml` — primary deploy pipeline
Trigger: `push` to `main`/`master` · Platform: `linux/arm64`
| Step | Description |
|---|---|
| `get-version` | Reads `package.json` version, writes `dev-<version>` to `.tags` |
| `typecheck` | `npm ci` + `npm run typecheck` — gates image build on clean TypeScript (cached npm on host) |
| `build-and-deploy` | `docker build -t git.tbs.amn.gg/escrow/backend:dev` locally on the agent, then `docker compose up -d --no-deps --pull never backend` — no registry push, image stays local |
| `notify` | Posts plain-text result to Telegram via `scripts/ci/tg-notify.cjs` (no parse_mode) |
> No registry push on production pipeline — agent is co-located with the stack; pushing large images over Tailscale times out.
### `development.yml` — parked
Trigger: `event: cron` (no cron configured — effectively disabled). Targets legacy `git.manko.yoga` registry and retired Arcane deploy. Use `manual.yml` for manual playground builds.
### `manual.yml` — manual build playground
Trigger: manual. Builds and pushes to `git.tbs.amn.gg/escrow/backend`. Used for testing the pipeline independently.
### `cleanup.yml` — image cleanup
Trigger: scheduled/manual. Removes old image tags from the registry.
**Important CI notes:**
- Always bump `package.json` version before pushing a CI-triggering commit — otherwise the build tag doesn't change and the deployed image may be stale.
- CI green does not guarantee the image was pushed — verify `git.tbs.amn.gg` has the `dev-<version>` tag before trusting the deploy.
- Woodpecker eats `${VAR}` in commands — use `$VAR` or `$$VAR`; prefer plugins over raw curl for notifications.
---
## 11. Local Development Quick-Start
```bash
# Clone and install
git clone git@git.tbs.amn.gg:escrow/backend.git
cd backend
npm install
# Copy environment file
cp .env.example .env.local
# Edit .env.local — set PG_URL, REDIS_URI, JWT_SECRET at minimum
# Start dependencies (Postgres + Redis)
docker compose -f docker-compose.local.yml up -d
# Run DB migrations
npx drizzle-kit migrate
# Start dev server (hot-reload)
npm run dev
# → listens on http://localhost:5001
# OR run in dev Docker
docker compose -f docker-compose.dev.yml up
# → listens on http://localhost:8080
# Seed database
npm run seed:users
npm run seed:categories
```
**Typecheck (required before push):**
```bash
npm run typecheck
```
A pre-push git hook blocks the push on tsc errors. If a parallel agent's mid-refactor tree has errors, use explicit `git add <path>` — never `git add -A`.
**Run tests:**
```bash
npm test
```
Test files live in `__tests__/`.
---
## 12. Environment Variables
| Variable | Description |
|---|---|
| `NODE_ENV` | `production` / `development` / `test` |
| `PORT` | HTTP listen port (default 5001) |
| `TRUST_PROXY_HOPS` | Number of reverse-proxy hops in front of app |
| `FRONTEND_URL` | Allowed CORS origin for frontend |
| `BACKEND_URL` | Public backend base URL |
| `PG_URL` | PostgreSQL connection string |
| `POSTGRES_USER` | Postgres username (Docker init) |
| `POSTGRES_PASSWORD` | Postgres password (Docker init) |
| `POSTGRES_DB` | Postgres database name (Docker init) |
| `MONGO_CONNECT_MODE` | `always` / `never` / `optional` — Mongo connection behavior (legacy) |
| `REDIS_URI` | Redis connection URI |
| `JWT_SECRET` | HS256 signing secret for access tokens |
| `JWT_EXPIRES_IN` | Access token TTL (e.g. `7d`) |
| `REFRESH_TOKEN_EXPIRES_IN` | Refresh token TTL (e.g. `30d`) |
| `ADMIN_EMAIL` | Bootstrap admin account email |
| `ADMIN_PASSWORD` | Bootstrap admin account password |
| `SEED_USERS` | `true` to auto-seed users on dev boot |
| `SEED_PASSWORD_ADMIN` | Admin seed account password |
| `SEED_PASSWORD_SUPPORT` | Support seed account password |
| `SEED_PASSWORD_BUYER` | Buyer seed account password |
| `SEED_PASSWORD_SELLER` | Seller seed account password |
| `GOOGLE_CLIENT_ID` | Google OAuth client ID |
| `GOOGLE_CLIENT_SECRET` | Google OAuth client secret |
| `WEBAUTHN_RP_ID` | WebAuthn relying party ID (e.g. `dev.amn.gg`) |
| `WEBAUTHN_RP_NAME` | WebAuthn relying party display name |
| `WEBAUTHN_RP_ORIGIN` | WebAuthn allowed origin |
| `SMTP_HOST` | SMTP server host |
| `SMTP_PORT` | SMTP server port |
| `SMTP_SECURE` | `true` for TLS |
| `SMTP_USER` | SMTP username |
| `SMTP_PASS` | SMTP password |
| `SMTP_FROM` | From address for outgoing email |
| `RESEND_WEBHOOK_SECRET` | Resend inbound webhook signing secret (`whsec_…`) |
| `TURNSTILE_SECRET_KEY` | Cloudflare Turnstile server-side secret (empty = CAPTCHA disabled) |
| `RATE_LIMIT_WINDOW_MS` | Rate limit window in milliseconds |
| `RATE_LIMIT_MAX_REQUESTS` | Max requests per window (global) |
| `MAX_FILE_SIZE` | Upload max file size in bytes |
| `UPLOAD_PATH` | Server-side upload directory |
| `PAYMENT_PROVIDER_MODE` | `live` / `test` |
| `PAYMENT_LEDGER_ENFORCEMENT` | `true` to enforce double-spend ledger guard |
| `ESCROW_WALLET_ADDRESS` | Platform escrow wallet address |
| `RECEIVER_WALLET_ADDRESS` | Platform receiver wallet address |
| `REQUEST_NETWORK_ENABLED` | Enable Request Network provider |
| `REQUEST_NETWORK_API_KEY` | Request Network API key |
| `REQUEST_NETWORK_NETWORK` | Target chain (`bsc`, `eth`, etc.) |
| `REQUEST_NETWORK_WEBHOOK_SECRET` | HMAC secret for RN webhook verification |
| `AMN_SCANNER_URL` | amn.scanner service base URL |
| `AMN_SCANNER_WEBHOOK_SECRET` | HMAC secret for scanner webhook verification |
| `AMN_SCANNER_DEFAULT` | `true` to make amn.scanner the default provider |
| `ORACLE_QUOTING_ENABLED` | Enable on-chain oracle pricing + depeg protection |
| `PRICE_ORACLE_PROVIDERS` | Comma-separated oracle providers (`chainlink,offchain_fx`) |
| `ORACLE_MAX_STALENESS_S` | Max oracle data age in seconds |
| `DEPEG_HARD_CAP_BPS` | Stablecoin depeg hard cap in basis points |
| `OFFCHAIN_FX_URL` | Off-chain FX rate source URL (required for IRR/TRY) |
| `CHAINLINK_RPC_1` | Private RPC override for Chainlink on ETH mainnet |
| `CHAINLINK_RPC_56` | Private RPC override for Chainlink on BSC |
| `DERIVED_DESTINATION_XPUB` | xPub for derived payment address derivation |
| `DERIVED_DESTINATION_SWEEP_SIGNER` | Sweep signing mode: `build-only` / `hot-key` / `kms` / `trezor` |
| `DERIVED_DESTINATION_SWEEP_INTERVAL_MS` | Sweep cron interval in ms (0 = disabled) |
| `SWEEP_MASTER_PRIVKEY` | Master sweep wallet private key (gas funder) |
| `TREZOR_SAFEKEEPING_REQUIRED` | `true` to require Trezor approval for admin actions |
| `TRANSACTION_SAFETY_ENABLED` | Enable transaction safety layer |
| `TRANSACTION_SAFETY_MIN_CONFIRMATIONS` | Minimum on-chain confirmations before release |
| `TRANSACTION_SAFETY_AML_PROVIDER` | AML provider: `none` / `ofac` / `chainalysis` |
| `CHAINALYSIS_API_KEY` | Chainalysis API key (when AML provider = chainalysis) |
| `TELEGRAM_BOT_TOKEN` | Telegram bot token |
| `TELEGRAM_WEBHOOK_SECRET_TOKEN` | Telegram webhook secret token header value |
| `TELEGRAM_INITDATA_MAX_AGE_SEC` | Max age for Telegram initData (default 86400 s) |
| `TG_NOTIFY_CHATS` | Comma-separated Telegram chat IDs for CI/admin notifications |
---
## 13. Known Issues / Open Items
| Issue | Status | Notes |
|---|---|---|
| Mongo→PG migration incomplete | In progress | Chat normalization is the current blocker; read cutover and backfill exec pending |
| Backend room-scoping for socket events | Open | Frontend provider gate is in place (v2.8.4); backend should scope payment events to `seller:<id>` rooms to prevent cross-user leakage |
| Rate limit counters are in-memory | Open | Not shared across instances; Redis adapter planned for distributed deployments |
| Oracle quoting disabled | Open | `ORACLE_QUOTING_ENABLED=false`; requires FX feed configuration before enabling |
| amn.scanner multi-seller + multi-chain gap | Open | Current scanner watches one chain; multi-seller and multi-chain support not yet verified |
| Woodpecker development.yml parked | Known | Targets legacy registry; needs repointing to `git.tbs.amn.gg` and new Arcane deploy before re-enabling |
| Trezor safekeeping off by default | By design | `TREZOR_SAFEKEEPING_REQUIRED=false`; must be enabled explicitly in production once admin xpub is registered |
| Request Network canonical proxy addresses | Known | RN's CREATE2 canonical-address claim is false for ETH and Base — probe actual address before trusting |
| JSON assets not copied to dist/ | Fixed (requires postbuild) | `tsc` does not copy `.json` files; explicit `postbuild` copy step required for any `fs.readFileSync` on JSON assets |
| Parallel agent push conflicts | Operational | mojtaba agent pushes to same branches; always `git fetch --rebase` before pushing; expect version-bump conflicts |