Architecture
App Router isolation, Postgres schema, Redis/BullMQ, encryption, and how failures stay contained.
Route groups
eListSync uses three Next.js App Router groups so marketing, operators, and admins do not share a shell:
(public)— landing, pricing, docs, privacy, terms, DPA(user)— dashboard, catalog, import, destinations, publisher, billing, support(admin)— analytics, queues, DLQ, resilience, support tooling
Middleware checks the session cookie (toksync_role). Public docs stay public. Operator routes require a user session. Admin routes require admin. Do not put catalog mutations on public routes.
Data plane (Postgres)
Core tables (names are indicative of the shipped schema):
organizations— tenantproducts/variants— canonical catalogdestination_accounts— connected shops, encrypted credentialschannel_listings— remote ids per channelchannel_sync_logs— success, retry, mapper vs API errorwebhook_events— inbound marketplace events with idempotency keysaudit_logs— operator actionsuser_api_keys— hashed keys for the browser extension
Row-level security isolates tenants. Queries in API handlers must run in the org context; never select another org's products by id alone.
Workers and queues
BullMQ (Redis) runs scrape, document ingest, and publish. **Bulkheads** mean each destination (and often scrape vs publish) has its own queue and concurrency cap.
- Rate limits: honor
Retry-After, jittered exponential backoff - Circuit breakers: closed → open → half-open on error budget
- DLQ: jobs that exhaust retries; Admin → System can replay or purge
This is why a failing Amazon app does not freeze Shopify publishes.
Security
- Secrets (OAuth refresh tokens, Woo keys, Bearer tokens) are encrypted with AES-GCM at rest
- Webhook signatures are compared with a timing-safe HMAC
- Extension ingest requires
x-toksync-api-keymatching a hasheduser_api_keysrow - SSRF guards reject private/link-local URLs on scrape and Drive import unless
TOKSYNC_ALLOW_LOCAL_INGEST=1for lab stores
Inventory locks
When two channels sell the same SKU at once, Redis Lua scripts decrement stock atomically. Duplicate webhooks are dropped using 24-hour idempotency keys on webhook_events. Architecture tests exercise concurrent buys for zero oversell — that is a property of this locking path, not a marketing slogan from a third party.
Unhealthy channels trip the breaker instead of applying stale stock.
Observability
Admin SSE (GET /api/traffic/stream) streams recent request events, latency, and RPS for the system terminal. Sync logs are the operator-facing diagnostic: if a publish failed, start there, not in a silent server log.