Files
nick-doc/00 - Overview/System Overview.md

15 KiB

title, tags, created
title tags created
System Overview
overview
architecture
system-map
mermaid
2026-05-23

System Overview

[!info] Scope This document gives you a single-page map of Amn from browser to blockchain. It is intentionally broad — the goal is for a new developer to walk away knowing what each major box does and how the boxes talk to each other. For deep dives into any subsystem, follow the wikilinks into 01 - Architecture.

The 10,000-foot view

Amn is a two-repo system:

  • Frontend (/Users/mojtabaheidari/code/frontend) — a Next.js 16 App Router application that serves the marketplace UI, the admin dashboard, the public blog, and the user-facing Web3 wallet flow.
  • Backend (/Users/mojtabaheidari/code/backend) — an Express 5 + TypeScript API server that owns all business logic, persists live app state primarily to MongoDB, caches in Redis, and brokers all external integrations. The active integration backend also contains the Postgres/Drizzle migration layer, but it is not yet the broad runtime store.

The two repos are deployable independently. They communicate over HTTPS (REST) for stateful actions and over WebSocket (Socket.IO) for live updates. The frontend never talks directly to MongoDB, Redis, Request Network API keys, OpenAI, or admin custody secrets -- every sensitive external interaction is mediated by the backend so that secrets stay on the server.

System map

flowchart TB
    subgraph Client["Client tier"]
        Browser["Browser<br/>(Chrome / Safari / mobile)"]
        Wallet["Wallet extension<br/>(MetaMask / WalletConnect)"]
    end

    subgraph FE["Frontend tier — Next.js 16"]
        SSR["Next.js SSR / RSC<br/>App Router"]
        ClientJS["Client JS<br/>MUI v7 + React 19"]
        Wagmi["Wagmi + Viem<br/>Web3 client"]
        SocketC["socket.io-client"]
        I18n["i18next<br/>6 locales + RTL"]
    end

    subgraph BE["Backend tier — Node.js / Express 5"]
        REST["REST API<br/>/api/*"]
        SocketS["Socket.IO server<br/>rooms per user / chat / request"]
        Auth["Auth service<br/>JWT + Passkey + Google + Telegram"]
        Market["Marketplace service<br/>Requests, Offers, Templates"]
        ChatSvc["Chat service"]
        PaySvc["Payment service<br/>Request Network + ledger + custody controls"]
        TelegramSvc["Telegram service<br/>bot + Mini App + notifications"]
        Disp["Dispute service"]
        Points["Points / Referrals"]
        BlogSvc["Blog service"]
        AISvc["AI service"]
        Notif["Notification service"]
        Files["File upload<br/>(multer + sharp)"]
    end

    subgraph Data["Data tier"]
        Mongo[("MongoDB<br/>via Mongoose<br/>primary runtime")]
        PG[("PostgreSQL 18<br/>Drizzle migration layer")]
        RedisDB[("Redis<br/>cache + locks")]
        Disk[("Local disk<br/>/uploads")]
    end

    subgraph External["External services"]
        Chain["EVM chains<br/>BSC / ETH / Polygon"]
        SMTP["SMTP<br/>(nodemailer)"]
        OpenAI["OpenAI API"]
        Google["Google OAuth"]
        Sentry["Sentry"]
        Alchemy["Alchemy RPC"]
        TelegramAPI["Telegram Bot API<br/>+ Mini App"]
        ReqNet["Request Network<br/>pay-in / webhooks"]
        CFWorker["Durable webhook ingress<br/>(roadmap)"]
    end

    Browser --> SSR
    Browser <--> ClientJS
    ClientJS <--> SocketC
    ClientJS --> Wagmi
    Wagmi <--> Wallet
    Wallet <--> Chain

    SSR --> REST
    ClientJS --> REST
    SocketC <--> SocketS

    REST --> Auth & Market & ChatSvc & PaySvc & TelegramSvc & Disp & Points & BlogSvc & AISvc & Notif & Files
    SocketS --> ChatSvc & Notif & Market

    Auth & Market & ChatSvc & PaySvc & Disp & Points & BlogSvc & TelegramSvc --> Mongo
    PaySvc -.->|oracle payment_quotes when enabled| PG
    Auth & PaySvc & Notif --> RedisDB
    Files --> Disk

    PaySvc <--> ReqNet
    ReqNet -.webhook.-> CFWorker
    CFWorker -.forward/replay.-> PaySvc
    PaySvc --> Chain
    PaySvc -.tx fetch.-> Alchemy

    TelegramSvc <--> TelegramAPI
    TelegramAPI -.webhook.-> TelegramSvc
    Auth --> TelegramAPI
    Notif --> SMTP
    Auth --> Google
    AISvc --> OpenAI
    BE --> Sentry
    FE --> Sentry

Walk-through of each subsystem

Authentication & identity — Authentication Flow

Auth is the gate to every authenticated route. Amn supports four login methods in parallel:

  • Email + password (JWT). Standard bcrypt-hashed credentials, access + refresh token pair, six-digit email verification codes, and password reset codes. Source: backend/src/services/auth/authService.ts.
  • Passkey (WebAuthn). Platform and cross-platform authenticators are registered against the user account; multiple devices per user are stored in User.passkeys[] (backend/src/models/User.ts:125).
  • Google OAuth. Server-side verification via google-auth-library. See backend/src/services/auth/googleOAuthService.ts.
  • Telegram (first-class). POST /api/auth/telegram accepts a Telegram Mini App initData string or a Telegram Login Widget payload. The backend verifies the Telegram HMAC signature, then signs the user in or auto-creates a new Amanat account with authProvider: "telegram" and no required email. This means a user who opens the Telegram Mini App is authenticated with zero sign-up friction. See backend/src/services/auth/authController.ts (telegramAuth) and Authentication Flow#Telegram first-class auth flow.

Roles are admin | buyer | seller (backend/src/models/User.ts:94). Role checks happen in route middleware. Refresh tokens are stored on the User document and rotated.

Marketplace — Marketplace Domain

The heart of the platform. Three first-class models drive it:

  • PurchaseRequest (backend/src/models/PurchaseRequest.ts) — the buyer's brief, with productType (physical_product | digital_product | service | consultation), budget range, urgency, delivery info (physical or online), and a long status enum that walks the entire deal: pending_payment → pending → active → received_offers → in_negotiation → payment → processing → delivery → delivered → confirming → completed.
  • SellerOffer (backend/src/models/SellerOffer.ts) — a single bid attached to a request with price, deliveryTime, status, validUntil, and free-form notes.
  • RequestTemplate (backend/src/models/RequestTemplate.ts) — a reusable "express checkout" version of a purchase request that can spawn real PurchaseRequest instances at click time.

Services live in backend/src/services/marketplace/ and are exposed through /api/marketplace/*. The frontend uses a mix of React Query (@tanstack/react-query) and SWR for data fetching, with mutations gated through the actions layer in frontend/src/actions/.

Payments -- Request Network, Ledger, And Custody Controls

Payments are where Amn is most distinctive. The live backend has converged on Request Network as the primary provider through a common Payment model (backend/src/models/Payment.ts) and provider-neutral adapter layer (backend/src/services/payment/adapters/):

  • Request Network pay-in -- /api/payment/request-network. Creates requests, exposes the Amanat in-house checkout block, and receives signed webhooks (x-request-network-signature). Pay-in service: requestNetworkPayInService.ts; reconciliation: requestNetworkReconciliationService.ts.
  • In-house wallet checkout -- buyer signs the RN-compatible approve + transferFromWithReferenceAndFee flow from their own wallet, so Rabby/MetaMask wallet UX stays inside Amanat.
  • Derived destination wallets -- /api/payment/derived-destinations admin endpoints manage per-(buyer, sellerOffer, chainId) receiving addresses, sweep status, and config health.
  • Funds ledger -- backend/src/services/payment/ledger/ tracks payment detection, holds, releases, refunds, fees, and adjustments independently of provider metadata.
  • Release/refund orchestration -- /api/payment/:id/(release|refund) builds instructions; /confirm records confirmed transaction hashes. Optional Trezor enforcement gates confirmation when TREZOR_SAFEKEEPING_REQUIRED=true.
  • Postgres migration layer -- backend 2.6.79 includes Drizzle migrations/repos and can persist oracle quote rows to payment_quotes when enabled. Payment records, ledger state, wallet destinations, and marketplace entities still flow through Mongo-backed services until the cutover work in Postgres Runtime Cutover Status is completed.

Historical SHKeeper and DePay docs remain in the vault for migration context, but the current backend tree no longer has backend/src/services/payment/shkeeper/. The current strategic path is in PRD - Decentralized Custody and Smart-Contract Escrow Roadmap.

Real-time chat — Chat System

Chat is built on Socket.IO rooms. Every entity that needs live updates gets its own room (see backend/src/app.ts:79-178):

  • user-<id> — personal notifications
  • chat-<id> — chat room messages, typing indicators, presence
  • request-<id> — purchase request lifecycle events
  • buyer-<id> / seller-<id> — marketplace-wide updates
  • sellers / buyers — global broadcast pools

Messages persist to MongoDB through the Chat model and are rate-limited per chat (chatRateLimiter.ts). The frontend's socket/ directory wraps socket.io-client and exposes typed event hooks to React components.

Notifications — Notifications

Two notification channels:

  • In-appNotification documents pushed over Socket.IO to user-<id> rooms; rendered in the frontend's bell-icon drawer.
  • Emailnodemailer + SMTP for verification codes, password resets, and high-importance events. See backend/src/services/email/.

Push and SMS are tracked as planned in backend/TODO.md.

Disputes — Dispute System

When a deal goes wrong (see Glossary#Dispute), either party can open a dispute. The backend creates a three-way chat between buyer, seller, and admin, opens a Dispute document with a structured timeline[] and evidence[], and can assign the dispute to an admin via assignAdmin(). Resolution can be refund | replacement | compensation | warning_seller | ban_seller | no_action in the current Mongoose model.

[!note] State alignment gap The dispute module exists now, but its model still uses the legacy pending | in_progress | resolved | ... enum. Funds Ledger and Escrow State Machine Specification defines the canonical future enum and financial side effects.

Points & referrals — Points System

Each user has an embedded points object (total | available | used | level) and referralStats (backend/src/models/User.ts). Every grant or spend writes a PointTransaction record for auditability. The points module supplies referral codes (the /r/:code short URL in app.ts:274 redirects to signup), tracks level progression against LevelConfig, and exposes the user-facing dashboard through frontend/src/sections/points/.

Blog — Blog System

A simple admin-authored CMS. BlogPost documents support categories, tags, and embedded video via TipTap's rich-text editor on the frontend. Public reads are unauthenticated (/api/blog/posts); writes require admin role. Seed data lives in backend/src/seeds/seedBlogPosts.ts and runs on dev startup.

AI — AI Assist

OpenAI (model configurable per call) is exposed through /api/ai/*. The current surface includes purchase-request drafting, chat summarisation, and admin-facing intent classification. Requests are queued from the frontend's actions/ai* modules and use streaming where appropriate.

File uploads

multer accepts multipart uploads (max 10 MB body, app.ts:232), sharp resizes images on the fly, and files land in /uploads (mounted as a static directory at app.ts:260). In production the path is configurable via UPLOAD_PATH.

Caching, locks & background jobs

Redis (backend/src/services/redis/) is used for:

  • short-lived caches (sessions, marketplace listings)
  • locks used by PaymentCoordinator to serialise status transitions
  • rate-limit counters (currently disabled in code but plumbed in)

Background workers run inside the Express process for now -- no separate worker tier. Notable timers:

  • startPendingPaymentsCleanup() — sweeps stale unpaid invoices
  • optional derived-destination sweep cron — sweeps eligible per-payment receiving addresses when configured
  • Request Network reconciliation — enabled via provider config when the rollout requires fallback status repair
  • Auto-seed logic on startup (gated by NODE_ENV and AUTO_SEED_ON_START)

Request lifecycle (the happy path)

[!example] End-to-end deal walk-through

  1. Buyer signs in (JWT). UI joins user-<buyerId> over Socket.IO.
  2. Buyer creates a Purchase RequestPOST /api/marketplace/requests. The request lands in pending/active. Sellers in the matching category receive a Socket.IO notification.
  3. Seller views the request, opens Seller Offer modal, submits price + delivery time → POST /api/marketplace/offers. Buyer sees the offer arrive live.
  4. Buyer accepts an offer → request moves to payment. UI opens the payment selector.
  5. Buyer picks Request Network -> backend creates a Payment and RN intent, returns an in-house checkout block, and the buyer signs the on-chain payment from their wallet.
  6. Request Network webhook/reconciliation plus the Transaction Safety Provider confirm tx hash, recipient, token, amount, and confirmations before the backend marks escrow funded.
  7. Seller ships. Buyer confirms delivery (or an admin resolves the order/dispute). Admin/custody owners execute release/refund through the release/refund instruction flow.
  8. Both parties leave reviews. Points are awarded. The deal is closed.

If the buyer disputes the delivery, jump to step 7 of the Dispute Flow instead.

Cross-cutting concerns

  • Observability — Sentry is initialised at the very top of app.ts (line 2-3) and on the frontend in sentry.{client,edge,server}.config.ts. Logs flow through backend/src/utils/logger.ts.
  • Securityhelmet, CORS scoped to FRONTEND_URL, JWT with bcrypt-hashed passwords, role-gated middleware. Rate limiting is active (10 req/15 min on auth, 30 on payment, 100 global; Request Network and Telegram webhooks are skip-listed to avoid false limits).
  • Internationalisation — Six locales (en, fr, vi, cn, ar, fa), with fa as default. RTL via stylis-plugin-rtl. See frontend/src/locales/.
  • Theming — MUI v7 with a custom theme in frontend/src/theme/. Dark mode is on the roadmap.
  • Containerisation — Docker Compose stacks for dev and prod live in both repos (docker-compose.dev.yml, docker-compose.production.yml).

Where to go next