Files
nick-doc/00 - Overview/System Overview.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

16 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 multi-repo workspace:

  • Frontend (frontend/) — a Next.js 16 App Router application that serves the marketplace UI, admin dashboard, public blog, Telegram Mini App shell, seller shop surfaces, and the white-label tenant admin UI.
  • Backend (backend/) — an Express 5 + TypeScript API server that owns business logic, persists runtime state through PostgreSQL/Drizzle repositories, caches in Redis, brokers external integrations, and now hosts the tenant/storefront/custom-domain APIs.
  • Deployment (deployment/) — Docker Compose, Caddy, migrations, and Gatus configuration for dev.amn.gg plus the escrow-multi / multi.amn.gg stack.
  • Scanner (scanner/) — the Go AMN Pay Scanner that watches chains and delivers signed payment webhooks back to the backend.
  • Amanat Assist (amanat-assist/) — the AI request-assistant Mini App and LLM proxy.
  • Documentation vault (nick-doc/) — Obsidian/Taskmaster documentation and audit history.

The deployable repos are versioned independently, but frontend/backend are kept in lockstep for image tags. They communicate over HTTPS (REST) for stateful actions and over WebSocket (Socket.IO) for live updates. The frontend never talks directly to PostgreSQL, Redis, scanner API keys, OpenAI, Telegram BotFather tokens, or admin custody secrets -- every sensitive external interaction is mediated by server-side services.

The active multi-shop branch is feature/white-label-shops in frontend/ and backend/. It powers multi.amn.gg, tenant subdomains, custom domains routed dynamically through Caddy, and tenant-owned Telegram bots. See Tenant, Tenant API, and Tenant Storefront Flow.

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"]
        TenantSvc["Tenant service<br/>host resolution + domain + bot"]
        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"]
        PG[("PostgreSQL 18<br/>Drizzle repositories")]
        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 & TenantSvc & ChatSvc & PaySvc & TelegramSvc & Disp & Points & BlogSvc & AISvc & Notif & Files
    SocketS --> ChatSvc & Notif & Market

    Auth & Market & TenantSvc & ChatSvc & PaySvc & Disp & Points & BlogSvc & TelegramSvc --> PG
    Auth & PaySvc & Notif --> RedisDB
    Files --> Disk

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

    TelegramSvc <--> TelegramAPI
    TenantSvc <--> 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 through the backend chat repository layer 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 record with a structured timeline/evidence model, and can assign the dispute to an admin. Resolution can be refund | replacement | compensation | warning_seller | ban_seller | no_action in the current service surface.

[!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

  • Tech Stack — exact versions of every dependency.
  • Roles & Personas — who does what in the system.
  • Glossary — a domain dictionary you will want open in another pane.
  • 01 - Architecture — service boundaries, module layout, and deployment topology.
  • 02 - Data Models — PostgreSQL/Drizzle tables plus legacy model references where still relevant.
  • 03 - API Reference — every endpoint, its payload, and its auth requirements.
  • 04 - Flows — diagrammed user journeys for every major use case.