FeedMe: a static RSS reader where an LLM writes the summaries
😋 AI-powered, Lightweight RSS Reader. Supports: GitHub Pages | Vercel | Alibaba Cloud ESA Pages | Docker
At a glance
- What is it?
- FeedMe is an MIT-licensed TypeScript project that crawls RSS feeds, asks an LLM for per-article summaries, and publishes the result as a static site. It is a good fit if you already pay for a model API and want a hosted reading page without a database or a login screen.
- Who is it for?
- Adopt FeedMe if you want a read-only, static digest page and are willing to pay a model provider per article and keep an API key in GitHub Secrets or a .env file. Skip it if you need per-user accounts, read/unread sync across devices, full-text search, or a reader that keeps working when the LLM endpoint is down.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 1 day ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
The problem FeedMe targets: reading many feeds without a backend
Most RSS readers are services. You create an account, the service stores your subscriptions and your read state, and you open their app or their web page. FeedMe inverts that. It has no login, no user table and no server-side state to speak of. The README describes the goal as skipping forced sign-in and app downloads, with a single responsive static page doing the reading. The output of a build is a set of files under public/data plus a static site, which is why the same artifact can sit on GitHub Pages, on Vercel, on Alibaba Cloud ESA Pages, or behind a local Docker container. The intended user is someone who already has a list of feeds, is comfortable editing a YAML file and setting environment variables, and would rather pay a model provider a small amount per article than pay a reader service a subscription. It is not aimed at people who want to discover feeds inside the app or who expect their phone and laptop to agree on which items they have already read.
Build-time summarisation: the pipeline from feed URL to static JSON
The architecture is a batch job, not a live reader. According to the README, the update step fetches RSS content, generates summaries and builds the static site in a single pass. In the Docker path this is exposed as two commands, pnpm update-feeds and pnpm build, run on a schedule by cron inside the container. In the GitHub path the same work happens inside the update-deploy.yml workflow, which runs hourly by default, on pushes, and on manual dispatch. The README states that update-feeds writes its results into public/data, and that the workflow then publishes the built site and also pushes the build output to a deploy branch so that Vercel and Alibaba Cloud ESA Pages can pick it up. That deploy branch is the integration seam: those two platforms are configured to watch deploy rather than main, and the README explicitly warns that the first Vercel deployment from main will fail. Summaries are generated per configured locale, controlled by SUMMARY_LOCALES, which defaults to zh,en. The summary prompt, the input truncation length, temperature, maxTokens and a fallback text used when summarisation fails are all adjustable under the summary key in src/config/feedme.config.yaml. The fallback field matters: it is the only documented behaviour for what a reader sees when a summarisation call does not succeed.
Configuration surface: feedme.config.yaml, environment variables and cron
There are three places where behaviour is set. The first is src/config/feedme.config.yaml. Each feed entry needs an id that is stable and unique, a name, a url and a category. The README notes that the display order of categories follows the order of the categories list, so ordering is a config decision rather than something the UI sorts. Two settings live under settings: defaultSource and maxItemsPerFeed. The second is environment. LLM_API_KEY, LLM_API_BASE and LLM_NAME are required for summarisation, and the README gives https://api.siliconflow.cn/v1 and THUDM/GLM-4-9B-0414 as example values for the base URL and model name, which suggests any OpenAI-compatible endpoint is intended to work. SUMMARY_LOCALES takes a comma-separated list. For Docker, the README says to copy .env.example to .env and fill those keys in, then run docker-compose up --build, after which the app answers on http://localhost:3000. The third is scheduling. On GitHub you edit the cron expression in .github/workflows/update-deploy.yml, for example to '0 0 * * *' for a nightly run. In Docker you edit src/config/crontab-docker instead, where the README gives '0 */6 * * *' as a six-hourly example. Local development follows the same shape: pnpm install, copy .env.example to .env, pnpm update-feeds, then pnpm typecheck and pnpm build, with pnpm dev serving on port 3000. Node.js 24 LTS is the stated requirement, with .nvmrc and .node-version provided for version switching.
Adding a summary language is more than editing SUMMARY_LOCALES
The README is clear that SUMMARY_LOCALES only selects which languages get generated. Adding a language that the interface does not already carry is a code change: you add locale metadata in src/config/i18n-config.ts and supply that language's strings in the relevant localisation configuration. Feed names are described as multilingual, so a new feed entry presumably needs a name per supported locale as well. This is the least automated part of the project and the one most likely to be underestimated. Someone who sets SUMMARY_LOCALES=ja expecting Japanese summaries and a Japanese interface will get the summaries and an interface that still speaks the languages already wired into i18n-config.ts. The README does not spell out the exact shape of the locale metadata or the string files, so read those two config files before promising a new language to anyone.
Where FeedMe is the wrong tool
Read state is the clearest gap. Nothing in the README describes marking items as read, syncing that state between devices, or storing per-user preferences. The page is a published digest, not a reader application with a database behind it. If you want your phone and your laptop to agree on what you have already seen, this is not that. Freshness is the second constraint. The default cadence is hourly on GitHub Actions and hourly inside the Docker container, and the content only changes when the job runs and the site is rebuilt and redeployed. A breaking news feed will lag by up to the cron interval plus the build time. Third, every summarised article is a paid API call. The README exposes maxItemsPerFeed, input truncation length and maxTokens, which are the levers on that cost, but it publishes no token or price figures, so the only way to know your bill is to run update-feeds against your own feed list and watch the provider dashboard. Fourth, the GitHub Actions path puts your LLM_API_KEY in repository secrets, which is fine for a personal fork but is a shared credential if you invite collaborators. Fifth, the summarisation step is a dependency on an external service at build time; the README documents a fallback string for failures rather than a retry or cache policy, so a bad provider day produces a build full of fallback text.
How FeedMe differs from a conventional self-hosted reader
The obvious comparison is a self-hosted reader such as Miniflux or FreshRSS. Those run a persistent server with a database, store your subscriptions and read state, expose an API, and render articles on request. They need a host that stays up, a database to back up, and an upgrade path when the schema changes. FeedMe has none of that: it produces files. The trade is that FeedMe gives up everything a database buys you. There is no read/unread state, no per-user accounts, no search index, and no way to subscribe from inside the app. What it adds instead is the summarisation pass and a hosting story that costs nothing on GitHub Pages. If your main frustration with conventional readers is the server you have to keep alive, FeedMe removes it. If your main frustration is that you cannot find what you read last month, FeedMe does not help. The two are not really substitutes; one is a service, the other is a scheduled publishing job.
Maintenance cost, licence and what to check before you fork
FeedMe is MIT licensed, which permits commercial use, modification and redistribution provided the copyright notice and permission notice are retained. That is a permissive licence, and it also means no warranty and no support obligation from the author. The last push recorded for the repository is 2026-09-10 and no releases were retrieved, so there is no tagged version to pin to; you are tracking main or a fork of it. The upgrade surface is small but not zero. Dependencies are managed with pnpm and Node.js 24 LTS is the stated runtime, so a Node major bump, a Vite or React major bump, or a change to the shape of src/config/feedme.config.yaml are the events that will require you to touch your fork. Because GitHub Actions pushes build output to a deploy branch, a workflow change and a config change can land in the same commit; review both before merging upstream changes into a fork that Vercel or ESA Pages is watching. The practical first step is to run pnpm update-feeds locally against your real feed list with a capped maxItemsPerFeed, then read the generated files in public/data to confirm the summaries are the quality and language you expect before you wire up any scheduled deployment.
Editorial conclusion
Adopt FeedMe if you want a read-only, static digest page and are willing to pay a model provider per article and keep an API key in GitHub Secrets or a .env file. Skip it if you need per-user accounts, read/unread sync across devices, full-text search, or a reader that keeps working when the LLM endpoint is down. Before deploying, verify three things: that your provider accepts the model name you put in LLM_NAME, that SUMMARY_LOCALES matches the locales present in src/config/i18n-config.ts, and that settings.maxItemsPerFeed in src/config/feedme.config.yaml keeps your hourly token spend where you want it.
Community notes