# P2P Poll App A peer-to-peer polling application where users can create polls, add options, and vote — all without a central server. Built for the [Evocracy democratic coding research experiment](https://demcode.evocracy.org/). ## How It Works Data is synchronized directly between peers using [Automerge](https://automerge.org/) CRDTs (Conflict-free Replicated Data Types). There is no backend database — every client holds a full copy of the poll document and changes merge automatically, even when made offline or concurrently. **Sync layers:** - **WebSocket** (`wss://sync.automerge.org`) — cross-device sync via a public relay - **BroadcastChannel** — instant cross-tab sync within the same browser - **IndexedDB** — local persistence across page reloads and offline use ## Requirements - [Bun](https://bun.sh/) (standalone runtime, no Node.js needed) ## Getting Started ```bash bun install # Install dependencies bun run dev # Start dev server (http://localhost:3000) ``` ## Usage 1. **Create a poll** — Enter a title on the home page and click "Create" 2. **Share it** — Copy the shareable link and send it to others 3. **Vote** — Click an option to vote; click again to unvote 4. **Add options** — Anyone with the link can add new poll options Each browser gets a stable peer ID (stored in localStorage) so votes are tracked per-device and double-voting is prevented. ## Tech Stack | Layer | Technology | |---|---| | Runtime | [Bun](https://bun.sh/) | | Framework | [Waku](https://waku.gg/) (React Server Components) | | Styling | Tailwind CSS v4 | | P2P sync | Automerge + automerge-repo | | Storage | IndexedDB (client-side) | ## Project Structure ``` src/ ├── pages/ # Waku page router │ ├── _root.tsx # HTML document shell │ ├── _layout.tsx # Root layout + Providers │ ├── index.tsx # Home page (create/join polls) │ └── poll/[id].tsx # Poll view page ├── components/ │ ├── Providers.tsx # Automerge Repo initialization │ ├── HomeClient.tsx # Create/join poll UI │ ├── PollPageClient.tsx # Poll ID validation │ ├── PollView.tsx # Poll display, voting, options │ └── ConnectionStatus.tsx # P2P connection indicator ├── lib/ │ ├── types.ts # Poll & PollOption interfaces │ ├── repo.ts # Automerge Repo singleton │ ├── poll.ts # Pure poll mutation functions │ ├── peer.ts # Peer ID management │ └── __tests__/ # Unit tests └── styles/ └── global.css # Tailwind CSS ``` ## Testing ```bash bun test ``` Covers poll creation, voting/unvoting, double-vote prevention, option management, and peer ID persistence. ## Architecture Notes - **Pure business logic** — Poll mutations in `src/lib/poll.ts` are pure functions, used inside Automerge's `changeDoc()` for CRDT-safe updates - **No server state** — The WebSocket relay only forwards sync messages; it never stores or processes poll data - **Offline-first** — The app works fully offline; changes sync when connectivity resumes - **Conflict-free** — Concurrent edits (e.g., two users voting at the same time) merge automatically without conflicts ## Built With This project was built in collaboration with [Claude Code](https://claude.ai/code), Anthropic's agentic coding tool. ## License MIT