The Tech Stack

For the technically inclined, I prepared the list of juicy details.

Infrastructure (AWS CDK)

- DynamoDB — Single-table design, pay-per-request, 4 GSIs (Domain, Type, Status, Slug), point-in-time recovery

- Lambda — Node.js 22, ARM64, 100+ handlers across 30 modules, esbuild bundled

- API Gateway HTTP v2 — Lambda authorizer, rate limiting (50 req/sec, burst 100), strict CORS

- CloudFront — 3 distributions (admin SPA, renderer, public assets CDN)

- S3 — 3 buckets (public assets, private resources with presigned URLs, renderer static + ISR warm cache)

- Cognito — 2 user pools: admin (invite-only, role/tenantId attributes) + public (self-signup, Google OAuth per tenant)

- SES — Contact forms, order confirmations, form notifications, Cognito invite emails

- EventBridge — Custom bus, all mutations publish audit events → worker Lambda → DynamoDB (with DLQ)

- Secrets Manager — Master API key + NextAuth secret

- Route 53 + ACM — DNS for root, wildcard, admin.*, api.*, tenant domains; regional + global certs

- CDK stacks: Main + 2 nested (Commerce, Engagement) to stay under CloudFormation's 500-resource limit

Renderer (Public Sites)

- Next.js 16 (React 19) — deployed via OpenNext 3 to Lambda

- Tailwind CSS v4 with PostCSS plugin (no traditional config file)

- NextAuth.js 4 — Google OAuth, dynamically configured per tenant from DynamoDB

- Middleware — tenant routing (domain → X-Forwarded-Host → rewrite), preview mode, referral tracking

- S3 warm cache — ISR pages cached in S3 (_cache/ prefix) for fast revalidation

- DOMPurify for HTML sanitization

- Direct DynamoDB reads — no API calls, server-side only

- CloudFront Function — preserves X-Forwarded-Host header for multi-tenant routing

Admin Panel (Control Panel SPA)

- React 19 + Vite 7

- shadcn/ui (Radix UI primitives: Dialog, Select, Dropdown, Label, Slot)

- Tailwind CSS v4 + tailwindcss-animate + @tailwindcss/typography

- Tiptap 3 — block-based editor with 19 custom plugins (split admin/render entry points)

- React Router 7 — client-side routing (39 pages)

- AWS Amplify 6 — Cognito auth integration

- React Flow + Dagre — content graph visualization

- lucide-react — icons

- Deployed to S3 + CloudFront with SPA fallback routing

Backend (Lambda Handlers)

- Node.js 22 (ARM64, esbuild, ESM with .js extensions)

- AWS SDK v3 — DynamoDB, Cognito, SES, EventBridge, S3, Secrets Manager

- aws-jwt-verify — token validation in Lambda authorizer

- fast-xml-parser — WordPress WXR import

- Vitest — tests against real staging DynamoDB

Shared Packages

- @amodx/shared — Zod 4 schemas, TypeScript types, country packs (single source of truth)

- @amodx/plugins — 19 block plugins (hero, pricing, image, contact, video, leadMagnet, cta, features, testimonials, columns, table, html, faq, postGrid, carousel, codeBlock, reviewsCarousel, categoryShowcase, markdown), highlight.js + marked for syntax highlighting

MCP Server (Claude Integration)

- MCP SDK — StdioServerTransport for Claude Desktop

- Playwright — browser automation (Chromium)

- Cheerio + Axios — web scraping (careful with that!)

- Full content CRUD + block schema knowledge embedded

Key Patterns

  • - Multi-tenancy: x-tenant-id on every request, PK-prefix isolation, never cross-tenant queries

  • - Single-table DynamoDB: adjacency lists (CATPROD#, CUSTORDER#), dual-write patterns (COUPONCODE#, FORMSLUG#), atomic counters

  • - Event-driven audit: all mutations → EventBridge → async worker (decoupled, with DLQ)

  • - Content versioning: automatic snapshots on every update, restore API

  • - Plugin architecture: split entry (admin.ts for Tiptap, render.ts for SSR-safe React), no cross-imports

  • - Country packs: 3-tier merge (English defaults → country pack → admin overrides)

  • - npm workspaces: build order shared → plugins → backend/admin/renderer