Zero-cost, ultra-premium daily tech news digest for software engineers. Runs fully serverless on GitHub. No hosting fees. No databases. No nonsense.
Live site: https://pranavworks100.github.io/fennec/
A Python script (scrape.py) runs once a day via GitHub Actions. It uses the Google Gemini API and LangGraph to:
public/news.jsonA Vite + React + TypeScript frontend hosted on GitHub Pages reads news.json as a static file — no backend, no API, zero cost.
| Layer | Technology |
|---|---|
| Frontend | Vite + React + TypeScript |
| Hosting | GitHub Pages (free) |
| Scraper | Python + Google Gemini 3.1 Flash Lite (via google-genai) |
| AI Orchestration | LangGraph |
| Data validation | Pydantic v2 |
| Automation | GitHub Actions |
| Cost | $0/month |
git clone https://github.com/pranavworks100/fennec.git
cd fennec
./dev.sh --setup
cp .env.example .env
# Edit .env and add your GEMINI_API_KEY
# Scrape fresh news AND start dev server (recommended)
./dev.sh
# Skip scraping, use existing news.json
./dev.sh --skip
# Or via npm
npm run start # scrape + dev server
npm run dev # just dev server
npm run scrape # just scraper
The app will be available at http://localhost:5173
Go to your repo → Settings → Pages → Source: GitHub Actions
Edit vite.config.ts and set base to your repo name:
base: "/fennec/",
Go to Settings → Secrets and variables → Actions → New repository secret
Name: GEMINI_API_KEY
Value: your_key_here
Or via GitHub CLI:
gh secret set GEMINI_API_KEY --body "your_gemini_api_key_here"
Notes:
GEMINI_API_KEY from repository secrets; do not store your key in the repo.Both workflows trigger automatically:
deploy.yml → builds and deploys the frontendscrape.yml → runs daily at 13:00 UTC to refresh news.json (scheduled cron)| Workflow | Trigger | What it does |
|---|---|---|
scrape.yml | Daily cron (13:00 UTC) + manual | Runs scrape.py, commits new news.json to main |
deploy.yml | Push to main | Builds Vite app, deploys to GitHub Pages |
When the scraper commits news.json, it triggers deploy.yml automatically — so the live site always reflects today's news within minutes.
To trigger a manual scrape: Actions → Daily News Scraper → Run workflow
The scraper is intentionally opinionated. Edit scrape.py to change:
MODEL — swap to gemini-3.1-flash-lite, gemini-2.0-flash, or gemini-1.5-profetch_signals prompt — change which topics/companies are includedcurate_and_format prompt — adjust the tone, wit level, or formatscrape.yml — currently set to 0 13 * * * (13:00 UTC); change the cron expression to a different UTC time if desiredfennec/
├── .github/
│ └── workflows/
│ ├── scrape.yml # Daily cron: runs scrape.py
│ └── deploy.yml # On push: builds + deploys to Pages
├── public/
│ ├── favicon.svg
│ ├── logo.png
│ ├── logo-dark.png
│ └── news.json # Generated daily by scrape.py
├── src/
│ ├── components/
│ │ ├── Header.tsx
│ │ ├── SceneOverview.tsx
│ │ ├── Feed.tsx
│ │ └── NewsCard.tsx
│ ├── types/
│ │ └── news.ts
│ ├── App.tsx
│ ├── main.tsx
│ └── index.css
├── scrape.py # Python scraper (HN + RSS + LangGraph + Gemini)
├── requirements.txt # Python dependencies (google-genai, langgraph, feedparser...)
├── dev.sh # Local dev helper (setup, scrape, start dev server)
├── .env.example # Example env vars (do NOT commit real secrets)
├── index.html
├── package.json
├── package-lock.json
├── vite.config.ts
├── tsconfig.json
└── tsconfig.node.json
MIT — fork it, ship it, make it yours.
Zero-cost, ultra-premium daily tech news digest for software engineers. Runs fully serverless on GitHub. No hosting fees. No databases. No nonsense.
Live site: https://pranavworks100.github.io/fennec/
A Python script (scrape.py) runs once a day via GitHub Actions. It uses the Google Gemini API and LangGraph to:
public/news.jsonA Vite + React + TypeScript frontend hosted on GitHub Pages reads news.json as a static file — no backend, no API, zero cost.
| Layer | Technology |
|---|---|
| Frontend | Vite + React + TypeScript |
| Hosting | GitHub Pages (free) |
| Scraper | Python + Google Gemini 3.1 Flash Lite (via google-genai) |
| AI Orchestration | LangGraph |
| Data validation | Pydantic v2 |
| Automation | GitHub Actions |
| Cost | $0/month |
git clone https://github.com/pranavworks100/fennec.git
cd fennec
./dev.sh --setup
cp .env.example .env
# Edit .env and add your GEMINI_API_KEY
# Scrape fresh news AND start dev server (recommended)
./dev.sh
# Skip scraping, use existing news.json
./dev.sh --skip
# Or via npm
npm run start # scrape + dev server
npm run dev # just dev server
npm run scrape # just scraper
The app will be available at http://localhost:5173
Go to your repo → Settings → Pages → Source: GitHub Actions
Edit vite.config.ts and set base to your repo name:
base: "/fennec/",
Go to Settings → Secrets and variables → Actions → New repository secret
Name: GEMINI_API_KEY
Value: your_key_here
Or via GitHub CLI:
gh secret set GEMINI_API_KEY --body "your_gemini_api_key_here"
Notes:
GEMINI_API_KEY from repository secrets; do not store your key in the repo.Both workflows trigger automatically:
deploy.yml → builds and deploys the frontendscrape.yml → runs daily at 13:00 UTC to refresh news.json (scheduled cron)| Workflow | Trigger | What it does |
|---|---|---|
scrape.yml | Daily cron (13:00 UTC) + manual | Runs scrape.py, commits new news.json to main |
deploy.yml | Push to main | Builds Vite app, deploys to GitHub Pages |
When the scraper commits news.json, it triggers deploy.yml automatically — so the live site always reflects today's news within minutes.
To trigger a manual scrape: Actions → Daily News Scraper → Run workflow
The scraper is intentionally opinionated. Edit scrape.py to change:
MODEL — swap to gemini-3.1-flash-lite, gemini-2.0-flash, or gemini-1.5-profetch_signals prompt — change which topics/companies are includedcurate_and_format prompt — adjust the tone, wit level, or formatscrape.yml — currently set to 0 13 * * * (13:00 UTC); change the cron expression to a different UTC time if desiredfennec/
├── .github/
│ └── workflows/
│ ├── scrape.yml # Daily cron: runs scrape.py
│ └── deploy.yml # On push: builds + deploys to Pages
├── public/
│ ├── favicon.svg
│ ├── logo.png
│ ├── logo-dark.png
│ └── news.json # Generated daily by scrape.py
├── src/
│ ├── components/
│ │ ├── Header.tsx
│ │ ├── SceneOverview.tsx
│ │ ├── Feed.tsx
│ │ └── NewsCard.tsx
│ ├── types/
│ │ └── news.ts
│ ├── App.tsx
│ ├── main.tsx
│ └── index.css
├── scrape.py # Python scraper (HN + RSS + LangGraph + Gemini)
├── requirements.txt # Python dependencies (google-genai, langgraph, feedparser...)
├── dev.sh # Local dev helper (setup, scrape, start dev server)
├── .env.example # Example env vars (do NOT commit real secrets)
├── index.html
├── package.json
├── package-lock.json
├── vite.config.ts
├── tsconfig.json
└── tsconfig.node.json
MIT — fork it, ship it, make it yours.