AI-powered neutral news aggregation across the political spectrum
The Meridian is a full-stack news aggregation platform that collects articles from 88 sources across the political spectrum and multiple global regions, clusters them by topic using OpenAI GPT-4o, and generates neutral, fact-first summaries with narrative divergence analysis.
Instead of reading one outlet's take, The Meridian shows you what multiple sources agree on, where they diverge, and what's missing from the conversation.
- Multi-source aggregation — RSS ingestion from 88 outlets spanning left, center-left, center, center-right, and right editorial perspectives
- AI-powered clustering — Articles about the same event are grouped into unified stories using semantic similarity
- Neutral summarization — GPT-4o generates balanced summaries with key facts, consensus scores, and divergence analysis
- Narrative Conflict Map — Interactive Leaflet map with Esri ArcGIS Dark Gray basemap visualizing global conflicts and geopolitical tensions where sources clash in coverage
- Edition system — Filter stories by region (US, UK, Canada, Europe, International) with browser geolocation support
- Bias indicators — Every source displays its editorial lean rating alongside its content
- Clustered story cards with neutral headlines and AI-generated summaries
- Consensus scoring (0–100) showing how much sources agree
- Source count per story with bias distribution indicators
- Topic filtering (Politics, World, Business, Technology, Science, Health, etc.)
- Edition-based regional filtering with location detection
- Full neutral summary with extracted key facts
- Divergence analysis highlighting where sources disagree
- Narrative lens breakdown showing how different outlets frame the story
- Coverage gap detection revealing what's missing from the conversation
- Direct links to all source articles
- Interactive world map powered by Leaflet with Esri ArcGIS World Dark Gray basemap tiles (authenticated via API key)
- Content-aware geocoding using 120+ real-world location database with weighted scoring algorithm
- Headline-priority matching: locations mentioned first in headlines are weighted 10x over summary mentions, with position bonus for tiebreaking
- Demonym support (e.g. "Kenyan", "Nigerian", "Brazilian") for accurate geographic placement
- Zoom-responsive markers with conflict intensity indicators
- Sidebar conflict list for keyboard-accessible navigation
- WCAG 2.1 AA compliant with semantic HTML, ARIA landmarks, and visible focus indicators
- Full-text search across story headlines and summaries
- Trending stories section highlighting the most-covered topics
- Related stories linked from each story detail page
- Bookmark/save stories for later reading (requires sign-in)
- Saved Stories page (
/saved) for quick access to bookmarked content
- Custom email/password authentication with bcrypt hashing (12 rounds)
- OAuth social login — Sign in with Google, Facebook, Apple, GitHub, or Microsoft via Passport.js
- Session-based auth with PostgreSQL session store
- Forgot/reset password flow with time-limited tokens
- Post-signup onboarding modal for topic preferences and digest frequency
- All content readable without signing in (no content gates)
- Scrolling market data ticker at the top of the page
- Manual pipeline trigger for on-demand article ingestion
- Contact form submission management with read/unread tracking
- Source monitoring and management
┌─────────────────────────────────────────────────────┐
│ Client (React) │
│ Vite · TypeScript · Tailwind · shadcn/ui · Wouter │
│ TanStack Query · Framer Motion · Leaflet │
└──────────────────────┬──────────────────────────────┘
│ HTTP/JSON
┌──────────────────────▼──────────────────────────────┐
│ Server (Express) │
│ TypeScript · express-session · connect-pg-simple │
│ bcrypt · rss-parser · OpenAI SDK │
└──────────────────────┬──────────────────────────────┘
│ Drizzle ORM
┌──────────────────────▼──────────────────────────────┐
│ PostgreSQL │
│ sources · articles · stories · story_articles │
│ users · sessions · password_reset_tokens │
│ contact_submissions │
└─────────────────────────────────────────────────────┘
├── client/ # React SPA
│ └── src/
│ ├── components/ # UI components (AuthModal, OnboardingModal, StoryCard, etc.)
│ │ ├── layout/ # Navbar, Footer
│ │ └── ui/ # shadcn/ui primitives
│ ├── hooks/ # Custom hooks (useAuth, useStories, useSources, useEdition)
│ ├── lib/ # Utilities (queryClient, apiRequest)
│ └── pages/ # Route pages (Feed, Story, ConflictMap, Admin, etc.)
├── server/ # Express API server
│ ├── pipeline.ts # RSS ingestion + AI clustering + summarization
│ ├── routes.ts # API route definitions + source registry
│ ├── storage.ts # Database access layer (IStorage interface)
│ └── replit_integrations/ # Auth, chat, image, audio, batch modules
│ └── auth/ # Custom auth (routes, storage, session config)
├── shared/ # Shared types and schemas
│ ├── schema.ts # Drizzle table definitions
│ ├── models/auth.ts # Auth-specific schema (users, sessions, reset tokens)
│ └── routes.ts # API route contracts with Zod validation
└── package.json
The Meridian aggregates from 88 sources across 12 regional categories:
| Region | Sources |
|---|---|
| US Mainstream | NYT, WSJ, AP, Fox News, NPR, Bloomberg, CNBC, OAN |
| UK | The Guardian, The Telegraph, The Independent, Sky News, Daily Mail, BBC UK, The Times UK |
| Canada | CBC News, Globe and Mail, National Post, CTV News, Toronto Star, Global News |
| Europe | Deutsche Welle, France 24, Euronews, Politico Europe, Der Spiegel International, The Local EU, EUobserver |
| Asia-Pacific | SCMP, Japan Times, Times of India, Straits Times, ABC News Australia, Nikkei Asia, The Diplomat, Channel News Asia, Kyodo News, The Hindu |
| Africa | Africa News, AllAfrica, The East African, Daily Maverick, Premium Times Nigeria, News24 South Africa, The Africa Report |
| Middle East | Al Jazeera, Middle East Eye, The National UAE, Haaretz, Arab News, Iran International |
| Latin America | Buenos Aires Herald, MercoPress, Mexico News Daily, Brazil Reports |
| Eastern Europe | The Moscow Times, Kyiv Independent, TASS English |
| Humanitarian | ReliefWeb, UN News, The New Humanitarian, ICRC News |
| Science & Tech | Ars Technica, TechCrunch, Wired, The Verge, Scientific American, Nature News, Science Daily, STAT News |
| Business | Bloomberg, CNBC, Financial Times, The Economist |
Each source is tagged with an editorial lean rating: left, center-left, center, center-right, or right.
The pipeline runs automatically every 30 minutes:
- Ingest — Fetch RSS feeds from all active sources, filter to articles published within the last 48 hours
- Deduplicate — Skip articles already in the database (matched by URL)
- Cluster — Group related articles into stories using OpenAI GPT-4o semantic similarity analysis
- Summarize — Generate neutral headlines, balanced summaries, and key facts for each story cluster
- Analyze — Produce consensus scores, narrative lens breakdowns, divergence summaries, and coverage gap reports
- Publish — Save stories with linked source articles to the database
| Technology | Purpose |
|---|---|
| React 18 | UI framework |
| TypeScript | Type safety |
| Vite 5 | Build tool and dev server with HMR |
| Tailwind CSS 3 | Utility-first styling |
| shadcn/ui (Radix UI) | Component library (New York style) |
| Wouter | Lightweight client-side routing |
| TanStack Query v5 | Server state management and caching |
| Framer Motion | Page transitions and animations |
| Leaflet + react-leaflet | Interactive conflict map |
| Esri ArcGIS Basemaps | Dark Gray world map tiles (authenticated) |
| date-fns | Date formatting |
| Lucide React | Icon library |
| Technology | Purpose |
|---|---|
| Express 4 | HTTP server and API routing |
| TypeScript (tsx) | Runtime TypeScript execution |
| Drizzle ORM | Type-safe database queries |
| PostgreSQL | Primary database |
| express-session | Session management |
| connect-pg-simple | PostgreSQL session store |
| bcrypt | Password hashing (12 salt rounds) |
| rss-parser | RSS feed ingestion |
| OpenAI SDK | GPT-4o for clustering and summarization |
| drizzle-zod | Schema-to-validation bridge |
| esbuild | Production server bundling |
- Node.js 20+
- PostgreSQL 16+
- OpenAI API key (for the AI pipeline)
| Variable | Required | Description |
|---|---|---|
DATABASE_URL |
Yes | PostgreSQL connection string |
SESSION_SECRET |
Yes | Express session signing secret |
AI_INTEGRATIONS_OPENAI_API_KEY |
Yes | OpenAI API key for GPT-4o |
AI_INTEGRATIONS_OPENAI_BASE_URL |
Yes | OpenAI API base URL |
ESRI_API_KEY |
No | Esri API key for authenticated basemap tile access |
GMAIL_APP_PASSWORD |
No | Gmail app password for email features |
# Install dependencies
npm install
# Push database schema
npm run db:push
# Start development server
npm run devThe app will be available at http://localhost:5000.
# Build for production
npm run build
# Start production server
npm start| Method | Endpoint | Description |
|---|---|---|
POST |
/api/auth/register |
Create account (email, password, firstName, lastName) |
POST |
/api/auth/login |
Sign in with email and password |
POST |
/api/auth/logout |
Destroy session |
GET |
/api/auth/user |
Get current authenticated user |
POST |
/api/auth/forgot-password |
Generate password reset token |
POST |
/api/auth/reset-password |
Reset password with valid token |
PATCH |
/api/auth/preferences |
Update topic preferences and digest frequency |
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/stories |
List published stories (supports pagination, region filter) |
GET |
/api/stories/search |
Search stories by query string |
GET |
/api/stories/trending |
Get trending stories by source coverage |
GET |
/api/stories/:id |
Get story detail with linked articles and source info |
GET |
/api/stories/:id/related |
Get related stories for a given story |
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/bookmarks |
List user's bookmarked stories |
POST |
/api/bookmarks/:storyId |
Bookmark a story |
DELETE |
/api/bookmarks/:storyId |
Remove a bookmark |
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/sources |
List all news sources with bias ratings |
| Method | Endpoint | Description |
|---|---|---|
POST |
/api/contact |
Submit contact form |
GET |
/api/contact/submissions |
List all submissions (admin) |
PATCH |
/api/contact/submissions/:id/read |
Mark submission as read |
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/config/map |
Get Esri API key for map tile authentication |
| Method | Endpoint | Description |
|---|---|---|
POST |
/api/pipeline/run |
Manually trigger the news pipeline |
| Route | Page | Description |
|---|---|---|
/ |
Landing | Hero section, featured stories, platform overview |
/feed |
Feed | Full story listing with topic and edition filters |
/story/:id |
Story Detail | Complete story with sources, divergence, and analysis |
/conflicts |
Conflict Map | Interactive geopolitical conflict visualization |
/saved |
Saved Stories | User's bookmarked stories (requires sign-in) |
/sources |
Sources Index | Browse all 88 news sources by bias rating |
/methodology |
Methodology | How the AI pipeline and neutrality engine work |
/admin |
Admin Dashboard | Pipeline control, contact submissions, monitoring |
/about |
About | Platform mission and team information |
/contact |
Contact | Contact form with subject categories |
/privacy |
Privacy Policy | Data handling and cookie policies |
/terms |
Terms of Service | Usage terms and conditions |
/auth/reset-password |
Password Reset | Token-based password reset form |
The Meridian follows WCAG 2.1 AA compliance standards:
- Semantic HTML with proper heading hierarchy (h1 → h2 → h3)
- ARIA landmarks, roles, and live regions throughout
- Visible focus indicators (focus:ring-2) on all interactive elements
- Minimum touch targets: 36×36px buttons, 44px list items
- Color contrast ratios meeting AA minimums
- Keyboard-accessible navigation for all features
- Conflict map sidebar provides equivalent keyboard access to map markers
This project is licensed under the MIT License.