A decentralized, embeddable shoutbox widget powered by XMTP for end-to-end encrypted messaging and GunDB for real-time presence. No backend server required β the entire system runs in the browser.
Web3 Shoutbox is a real-time chat widget that any website can embed via an <iframe>. Identity is anchored to crypto wallets, all messages are end-to-end encrypted via XMTP's MLS protocol, and presence (who is online) is tracked peer-to-peer through GunDB. Zero server infrastructure required β just static hosting.
- π End-to-end encryption β MLS protocol (RFC 9420) via XMTP, mandatory on all messages
- π Wallet-based identity β connect with MetaMask, Rainbow, Coinbase, or any WalletConnect wallet
- π₯ Real-time presence β see who is online via GunDB CRDT-based peer-to-peer sync
- π¦ Embeddable widget β drop an
<iframe>on any website with a single line of HTML - π Sliding window groups β XMTP groups rotate on a schedule, naturally resetting member counts
- ποΈ No backend β fully JAMstack, deploy to any static host
- π³οΈ Deterministic leader election β client-side, coordination-free group creation
- π Dark mode β full light/dark theme support
- π± Mobile responsive β works down to 280px width
- π Toast notifications β real-time feedback for connections, errors, and transitions
-
Clone the repository
git clone <repository-url> cd web3-shoutbox-platform
-
Install dependencies
npm install
-
Configure environment variables
cp .env.local.example .env.local # Edit .env.local β you need a WalletConnect Project ID at minimum -
Run the development server
npm run dev
-
Open in browser Navigate to http://localhost:3000
Add this to any website to embed the shoutbox:
<iframe
src="https://your-shoutbox-domain.com/embed/shoutbox"
width="400"
height="600"
frameborder="0"
style="border-radius: 12px; box-shadow: 0 4px 24px rgba(0,0,0,0.12);"
allow="clipboard-write">
</iframe>The widget auto-detects the parent page URL as the chat room. Pass ?room=my-room&theme=dark for customization.
π Full Embed Guide β
| Technology | Version | Purpose |
|---|---|---|
| Vite | 7.x | Build tool & dev server |
| React | 19.x | UI library |
| TypeScript | 5.x | Type safety |
| Tailwind CSS | 4.x | Styling |
| XMTP browser-sdk | 7.x | E2E encrypted messaging (MLS) |
| GunDB | 0.2020.x | CRDT-based real-time presence |
| wagmi | 3.x | Ethereum React hooks |
| viem | 2.x | Ethereum library |
| @reown/appkit | 1.x | Wallet connection (WalletConnect) |
| Zustand | 5.x | State management |
| Zod | 4.x | Runtime validation |
| Sonner | 2.x | Toast notifications |
The shoutbox is a dual-protocol, browser-only system:
- XMTP handles encrypted message transport β messages are signed with wallet keys and encrypted via MLS
- GunDB handles ephemeral presence β who is currently on the page, tracked via heartbeat TTL
These layers are intentionally decoupled. GunDB owns the UI roster; XMTP owns message delivery. Presence changes do not trigger XMTP group mutations.
Groups use a sliding window model β instead of one permanent group per URL, groups rotate on a time-based schedule (default: 5 minutes). This avoids the 250-member cap and eliminates expensive removeMembers MLS operations.
π Full Architecture Doc β
web3-shoutbox-platform/
βββ src/
β βββ components/
β β βββ auth/ # ConnectWallet button
β β βββ chat/ # ChatContainer, MessageList, MessageBubble, MessageInput
β β βββ layout/ # AppLayout, Header
β β βββ presence/ # PresencePanel, UserAvatar
β β βββ providers/ # Web3Provider, XmtpProvider, GunProvider, ThemeProvider
β β βββ ui/ # Skeleton, XmtpStepIndicator
β β βββ ErrorBoundary.tsx # Top-level error boundary
β βββ config/
β β βββ env.ts # Zod-validated environment variables
β βββ hooks/
β β βββ useEmbed.ts # Embed detection, PostMessage API, auto-resize
β β βββ useGroupLifecycle.ts # Window management, leader election orchestration
β β βββ useLeaderElection.ts # Deterministic leader computation
β β βββ useOnlineUsers.ts # Presence subscription β OnlineUser[]
β β βββ usePresence.ts # Join/leave room, heartbeat lifecycle
β β βββ useShoutboxRoom.ts # Unified room hook (presence + messaging + groups)
β β βββ useXmtpClient.ts # XMTP client state from provider
β β βββ useXmtpConversation.ts # Group message send/receive
β βββ lib/
β β βββ embed-messaging.ts # PostMessage protocol, auto-resize, config parsing
β β βββ group-lifecycle.ts # GunDB group read/write/subscribe
β β βββ gun.ts # GunDB singleton instance
β β βββ gun-presence.ts # Low-level presence read/write
β β βββ leader-election.ts # Deterministic leader election algorithm
β β βββ retry.ts # Generic retry with exponential backoff
β β βββ url-utils.ts # URL normalization + SHA-256 room keys
β β βββ utils.ts # Tailwind cn() helper
β β βββ xmtp.ts # XMTP client factory
β βββ pages/
β β βββ embed/
β β β βββ EmbedShoutboxPage.tsx # Compact embed widget page
β β βββ NotFoundPage.tsx
β β βββ RoomBrowserPage.tsx
β β βββ SettingsPage.tsx
β β βββ ShoutboxPage.tsx # Main standalone shoutbox page
β βββ services/
β β βββ groupLifecycleService.ts # Group creation, discovery, failover
β β βββ messagingService.ts # XMTP send/receive with retry
β β βββ presenceService.ts # GunDB presence join/leave/heartbeat
β βββ stores/
β β βββ authStore.ts # Wallet connection state
β β βββ chatStore.ts # Messages and chat UI state
β β βββ presenceStore.ts # Online users state
β βββ types/
β β βββ embed.ts # PostMessage event/command types
β β βββ errors.ts # Typed error classes + error classifiers
β β βββ group.ts # GroupWindow, GroupState
β β βββ gun.d.ts # GunDB type declarations
β β βββ message.ts # ShoutboxMessage
β β βββ presence.ts # PresenceRecord, OnlineUser
β β βββ result.ts # Result<T, E> pattern (ok/err)
β βββ App.tsx # Router + provider tree
β βββ globals.css # Tailwind base + custom animations
β βββ main.tsx # Entry point
βββ public/
β βββ test-embed.html # Interactive embed test page
β βββ _headers # CDN headers for iframe embedding
β βββ _redirects # SPA fallback redirects
βββ docs/ # Documentation
β βββ ARCHITECTURE.md
β βββ CONTRIBUTING.md
β βββ EMBED_GUIDE.md
β βββ SETUP.md
βββ e2e/ # Playwright E2E test directory
βββ .env.local.example # Environment variable template
βββ eslint.config.mjs # ESLint configuration
βββ index.html # Vite entry HTML
βββ package.json # Dependencies & scripts
βββ playwright.config.ts # Playwright configuration
βββ postcss.config.mjs # PostCSS (Tailwind)
βββ tsconfig.json # TypeScript configuration
βββ vite.config.ts # Vite configuration
βββ vitest.config.ts # Vitest configuration
| Command | Description |
|---|---|
npm run dev |
Start development server on port 3000 |
npm run build |
Type-check and build for production (output: out/) |
npm run preview |
Preview production build locally |
npm run test |
Run unit tests with Vitest |
npm run test:watch |
Run unit tests in watch mode |
npm run test:e2e |
Run Playwright E2E tests |
npm run lint |
Run ESLint |
Create a .env.local file from the template:
cp .env.local.example .env.local| Variable | Required | Default | Description |
|---|---|---|---|
VITE_WALLETCONNECT_PROJECT_ID |
Yes | β | WalletConnect Project ID from Reown Dashboard |
VITE_XMTP_ENV |
Yes | β | XMTP network environment: dev or production |
VITE_APP_URL |
Yes | β | Application base URL (e.g., http://localhost:3000) |
VITE_GUN_RELAY_PEERS |
No | Public relays | Comma-separated GunDB relay peer URLs |
VITE_SLIDING_WINDOW_MINUTES |
No | 5 |
Duration of each sliding window epoch in minutes |
The shoutbox is a static site β build it and deploy the out/ directory to any static host.
npm run buildnpx vercel --prodSet the build output directory to out and add environment variables in the Vercel dashboard.
The public/_redirects file handles SPA routing automatically. Deploy via:
npx netlify deploy --prod --dir=outThe public/_headers file configures iframe embedding headers. Connect your repository in the Cloudflare Pages dashboard with:
- Build command:
npm run build - Build output directory:
out
- Architecture β System design, data flow, and key decisions
- Embed Guide β Third-party integration guide with PostMessage API
- Setup Guide β Detailed environment setup and troubleshooting
- Contributing β Code style, workflow, and PR guidelines
MIT License β see LICENSE for details.
