News that matter to humanity. Curated with care by AI.
Crawls news sources, assesses relevance with structured AI analysis, and publishes curated stories.
- Frontend: Vite + React 18 + TypeScript + Tailwind CSS
- Backend: Express + TypeScript + LangChain + OpenAI
- Database: PostgreSQL + pgvector (Prisma ORM)
- Deployment: Render.com
- Node.js 18+ and npm
- PostgreSQL 15+ (with pgvector extension)
- OpenAI API key
-
Clone the repository:
git clone https://github.com/OdinMB/actually-relevant.git cd actually-relevant -
Install dependencies:
cd client && npm install cd ../server && npm install
-
Set up the database:
# Create the database createdb actually_relevant # Enable pgvector extension (connect to DB first) psql actually_relevant -c 'CREATE EXTENSION IF NOT EXISTS vector;'
-
Configure environment variables:
# Create server/.env with at minimum these required variables: # DATABASE_URL, OPENAI_API_KEY, JWT_SECRET, FRONTEND_URL # See server/src/config.ts for all available settings and their defaults.
-
Run database migrations:
cd server && npx prisma migrate dev
-
Start development servers:
# Terminal 1 — Frontend (localhost:5173) cd client && npm run dev # Terminal 2 — Backend (localhost:3001) cd server && npm run dev
This project requires three services on Render: a managed PostgreSQL database, an Express backend (web service), and a React frontend (static site). The backend and frontend run on separate origins.
- Create a new PostgreSQL instance on Render
- Note the Internal Database URL (used by the backend service)
- Enable pgvector — connect to the database and run:
CREATE EXTENSION IF NOT EXISTS vector;
| Field | Value |
|---|---|
| Root Directory | server |
| Build Command | npm install --include=dev && npx prisma generate && npx prisma migrate deploy && npm run build |
| Start Command | npm start |
| Health Check Path | /health |
The build generates the Prisma client, applies any pending database migrations, and compiles TypeScript. Migrations run automatically on every deploy via prisma migrate deploy, which is a no-op when there are no pending migrations.
Environment Variables:
| Variable | Required | Description |
|---|---|---|
DATABASE_URL |
Yes | Internal PostgreSQL connection string from step 1 |
OPENAI_API_KEY |
Yes | OpenAI API key for LLM analysis |
FRONTEND_URL |
Yes | Frontend URL for CORS (e.g. https://actuallyrelevant.news) |
JWT_SECRET |
Yes | Random string (32+ chars) for signing auth tokens |
NODE_ENV |
Yes | Set to production (enables secure cross-origin cookies) |
PORT |
No | Render sets this automatically (defaults to 10000) |
PUBLIC_API_KEY |
No | Static API key for public consumers (mobile apps, etc.) |
LOG_LEVEL |
No | Logging verbosity (default: info) |
Architecture notes:
- Cron jobs run in-process via node-cron — no separate worker service is needed. Job configuration lives in the
job_runsdatabase table and is managed from the admin dashboard. - Graceful shutdown handles
SIGTERM(sent by Render on deploy) by draining in-flight LLM tasks before disconnecting from the database. - Reverse proxy trust is configured (
trust proxy: 1) for correct client IP detection behind Render's load balancer. - Cross-origin cookies use
sameSite: 'none'+secure: truein production, which is required because the frontend and backend are on different Render origins. This is whyNODE_ENV=productionis mandatory.
| Field | Value |
|---|---|
| Root Directory | client |
| Build Command | npm install && npm run build |
| Publish Directory | dist |
The build type-checks, bundles with Vite, and prerenders public routes using Puppeteer. Render's build environment includes Chromium, so prerendering works without extra setup.
Rewrite rules: Add these rewrites in the Render dashboard in this exact order (Render evaluates rules top-to-bottom, first match wins):
| Source | Destination | Action |
|---|---|---|
/sitemap.xml |
https://<backend-service>.onrender.com/api/sitemap.xml |
Rewrite |
/* |
/index.html |
Rewrite |
Order is critical: The /sitemap.xml rule must appear before the catch-all /* rule. If reversed, the catch-all matches first and serves the SPA shell, resulting in a 404.
The sitemap rewrite proxies requests to the backend, which generates the sitemap dynamically from published stories. No static sitemap.xml file should exist in client/public/ — Render serves static files before applying rewrite rules.
Environment Variables:
| Variable | Required | Description |
|---|---|---|
VITE_API_URL |
Yes | Backend URL (e.g. https://api.actuallyrelevant.news) |
- Set backend
FRONTEND_URLto match the frontend URL (and vice versa forVITE_API_URL) - Database migrations run automatically during the build step — no manual action needed
- Create the first admin user from the Render shell:
npx tsx src/scripts/create-admin.ts
- Add the
/sitemap.xmlrewrite rule to the static site (see Frontend section above) - Verify the health endpoint:
curl https://<backend-url>/health - Verify the sitemap:
curl https://<frontend-url>/sitemap.xml
actually-relevant/
├── client/ # React frontend
│ ├── src/ # Source code
│ ├── scripts/ # Build scripts (sitemap, images)
│ ├── dist/ # Built output (gitignored)
│ └── package.json
├── server/ # Express backend
│ ├── src/ # Source code
│ ├── prisma/ # Database schema and migrations
│ ├── dist/ # Built output (gitignored)
│ └── package.json
├── shared/ # Shared types and constants
├── .context/ # Implementation documentation (17 files)
├── .specs/ # Behavioral specifications (Allium)
├── CONTRIBUTING.md # Contribution guidelines
├── LICENSE # AGPL v3
└── README.md # This file
Contributions are welcome! See CONTRIBUTING.md for guidelines, including how to set up the development environment, submit pull requests, and the project's lightweight contributor agreement.
Actually Relevant is actively seeking a long-term institutional owner in journalism, civic tech, or effective altruism. If your organization could give this project a home, visit actuallyrelevant.news/stewardship to learn more.
This project is licensed under the GNU Affero General Public License v3.0. Organizations interested in running actuallyrelevant.news as a long-term steward can receive more accommodating license terms — see Stewardship.
Check the build logs. Common issues:
- Missing
Root Directorysetting on Render - Node version mismatch — add
enginesto package.json if needed - Missing
npx prisma generatebefore server build
- Verify
FRONTEND_URLis set correctly on the backend - Ensure it matches exactly (including
https://, no trailing slash) - Redeploy after changing environment variables
- Verify
DATABASE_URLis correct - Ensure pgvector extension is installed:
CREATE EXTENSION IF NOT EXISTS vector; - Run migrations:
npx prisma migrate deploy
curl https://your-api-url.onrender.com/health
# Should return: {"status":"ok"}