Go to App
getting-started/architecture

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 — tenant
  • products / variants — canonical catalog
  • destination_accounts — connected shops, encrypted credentials
  • channel_listings — remote ids per channel
  • channel_sync_logs — success, retry, mapper vs API error
  • webhook_events — inbound marketplace events with idempotency keys
  • audit_logs — operator actions
  • user_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-key matching a hashed user_api_keys row
  • SSRF guards reject private/link-local URLs on scrape and Drive import unless TOKSYNC_ALLOW_LOCAL_INGEST=1 for 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.