- 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>
16 KiB
title, tags, created
| title | tags | created | ||||
|---|---|---|---|---|---|---|
| System Overview |
|
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 fordev.amn.ggplus theescrow-multi/multi.amn.ggstack. - 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. Seebackend/src/services/auth/googleOAuthService.ts. - Telegram (first-class).
POST /api/auth/telegramaccepts a Telegram Mini AppinitDatastring 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 withauthProvider: "telegram"and no required email. This means a user who opens the Telegram Mini App is authenticated with zero sign-up friction. Seebackend/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 withprice,deliveryTime,status,validUntil, and free-formnotes. - RequestTemplate (
backend/src/models/RequestTemplate.ts) — a reusable "express checkout" version of a purchase request that can spawn realPurchaseRequestinstances 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+transferFromWithReferenceAndFeeflow from their own wallet, so Rabby/MetaMask wallet UX stays inside Amanat. - Derived destination wallets --
/api/payment/derived-destinationsadmin 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;/confirmrecords confirmed transaction hashes. Optional Trezor enforcement gates confirmation whenTREZOR_SAFEKEEPING_REQUIRED=true. - Postgres migration layer -- backend
2.6.79includes Drizzle migrations/repos and can persist oracle quote rows topayment_quoteswhen 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 notificationschat-<id>— chat room messages, typing indicators, presencerequest-<id>— purchase request lifecycle eventsbuyer-<id>/seller-<id>— marketplace-wide updatessellers/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-app —
Notificationdocuments pushed over Socket.IO touser-<id>rooms; rendered in the frontend's bell-icon drawer. - Email —
nodemailer+ SMTP for verification codes, password resets, and high-importance events. Seebackend/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
PaymentCoordinatorto 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_ENVandAUTO_SEED_ON_START)
Request lifecycle (the happy path)
[!example] End-to-end deal walk-through
- Buyer signs in (JWT). UI joins
user-<buyerId>over Socket.IO.- Buyer creates a Purchase Request →
POST /api/marketplace/requests. The request lands inpending/active. Sellers in the matching category receive a Socket.IO notification.- Seller views the request, opens Seller Offer modal, submits price + delivery time →
POST /api/marketplace/offers. Buyer sees the offer arrive live.- Buyer accepts an offer → request moves to
payment. UI opens the payment selector.- 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.
- Request Network webhook/reconciliation plus the Transaction Safety Provider confirm tx hash, recipient, token, amount, and confirmations before the backend marks escrow funded.
- Seller ships. Buyer confirms delivery (or an admin resolves the order/dispute). Admin/custody owners execute release/refund through the release/refund instruction flow.
- 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 insentry.{client,edge,server}.config.ts. Logs flow throughbackend/src/utils/logger.ts. - Security —
helmet, CORS scoped toFRONTEND_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
faas default. RTL viastylis-plugin-rtl. Seefrontend/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.