Real-time monitoring dashboard for the Lantern censorship circumvention network. Visualizes global proxy traffic, volunteer contributions, protocol performance, and per-ISP blocking patterns powered by the EXP3.S bandit system.
Interactive World Map — Animated traffic arcs flow from censored regions to volunteer proxies. Click any country to drill into ISP-level metrics: arm counts, block rates, entropy, and traffic distribution. 13 censored countries are highlighted with per-city scatter nodes weighted by population.
Live Network Stats — Active volunteers, users reached, blocks evaded, and bandwidth consumed. Stats update every 30 seconds from the Lantern API, with animated number transitions and country/ISP filtering.
Proxy Volunteering — Share your connection directly from the dashboard. Integrates the unbounded WASM proxy via a headless API. Tracks session history across browser restarts and shows live connection count, throughput, and lifetime stats.
"My Proxy" View — Toggle to see only your proxy's impact: your geo-located position, connected peers, and traffic arcs flowing through you. Peer IPs are resolved via WebRTC ICE candidates.
Protocol Feed — Streaming event log of protocol generation, deployment, blocking, and evasion across the network.
Impact Card — Personal volunteer stats with a rank progression system (Ember through Lighthouse), streak tracking, and countries helped.
Demo Mode — Click "LIVE DATA" to toggle synthetic data with realistic jitter for development and demos when the API is unavailable.
# Clone
git clone https://github.com/getlantern/lantern-dashboard.git
cd lantern-dashboard
# Install
npm install
# Configure environment
cp .env.example .env
# Edit .env with your API URL and Google OAuth client ID
# Dev server (http://localhost:5173)
npm run dev| Variable | Description | Example |
|---|---|---|
VITE_API_URL |
Lantern API base URL | https://api.staging.iantem.io |
VITE_GOOGLE_CLIENT_ID |
Google OAuth 2.0 client ID | 123.apps.googleusercontent.com |
Create the OAuth client at GCP Console > APIs & Services > Credentials. Set authorized JavaScript origins to include http://localhost:5173 and your deploy domain.
npm run dev # Vite dev server with HMR
npm run build # TypeScript check + Vite production build
npm run preview # Preview production build locally
npm run lint # ESLintsrc/
├── components/
│ ├── Dashboard.tsx Main layout: map, stats, right panel
│ ├── WorldMap.tsx SVG map with arcs, scatter, ISP panel
│ ├── ProxyWidget.tsx Volunteer proxy toggle + live stats
│ ├── StatsRow.tsx Animated stat cards with filtering
│ ├── ImpactCard.tsx Personal volunteer rank & stats
│ ├── ProtocolFeed.tsx Real-time protocol event stream
│ └── LoginScreen.tsx Google OAuth login
├── hooks/
│ ├── useLiveData.ts API polling, demo mode, mock fallback
│ ├── useProxy.ts WASM proxy lifecycle & session tracking
│ ├── useAuth.tsx OAuth flow, token refresh, domain check
│ └── useGeoLookup.ts IP geolocation with caching
├── api/
│ └── client.ts Typed API client (v1/dashboard/*)
├── data/
│ └── mock.ts Synthetic data generation
├── App.tsx Root with auth routing
├── App.css Component styles
└── index.css CSS variables, keyframes, layout
-
useLiveDatapolls/v1/dashboard/globalevery 30s, returning per-country bandit stats (ASN counts, block rates, entropy). Falls back to mock data if the API is unreachable. Demo mode generates synthetic data with jitter. -
WorldMaprenders countries colored by block rate. Clicking a country fetches/v1/dashboard/asns?country=XXfor ISP drill-down. Arcs animate from censored cities to proxy regions using hash-based stable timing. -
useProxyloads the unbounded WASM embed script, initializeswindow.LanternProxy, and subscribes to connection/throughput events. Session time persists to localStorage with crash recovery viabeforeunload. -
useGeoLookupresolves proxy peer IPs to lat/lng viageo.getiantem.org/lookup. Lookups are deferred until the proxy is running and cached per-IP with in-flight deduplication.
| Endpoint | Returns |
|---|---|
GET /v1/dashboard/global |
Countries with ASN count, avg block rate, avg entropy |
GET /v1/dashboard/asns?country=XX |
Per-ISP metrics: arms, blocks, pulls, entropy, top arms |
GET /v1/dashboard/asn-history?asn=AS123 |
Historical snapshots for one ASN |
GET /v1/dashboard/blocked-routes |
Currently blocked route IDs |
Google OAuth 2.0 restricted to @getlantern.org domain. Tokens are silently refreshed via Google One Tap on 401 responses. User info persists in localStorage.
The _redirects file handles SPA routing. Connect the repo to Cloudflare Pages with:
- Build command:
npm run build - Build output:
dist - Environment variables:
VITE_API_URL,VITE_GOOGLE_CLIENT_ID
A firebase.json is included for Firebase Hosting as an alternative.
| Framework | React 19, TypeScript 5.9 |
| Build | Vite 7.3 |
| Map | react-simple-maps (D3-geo) |
| Charts | Recharts |
| Auth | @react-oauth/google |
| Animation | Framer Motion, CSS keyframes |
| Fonts | IBM Plex Mono, Outfit |
| Proxy | unbounded WASM headless API |
Dark theme with cyan (#00e5c8) primary accent and amber (#f0a030) secondary. CSS custom properties for theming, no CSS-in-JS. Responsive layout: side-by-side on desktop, stacked on mobile.