# FeedCraft turns an RSS URL into a pipeline by prefixing it, and calls the pipeline a Craft

> FeedCraft is a Go middleware that sits between your reader and your feeds: it extracts full text, translates, summarises with an OpenAI compatible model, filters by natural language, and can also build feeds from web pages, JSON APIs, and search results. The interface is deliberately a URL, so a feed you read in English can become a translated feed by changing its address, while self-hosters get a console for deeper configuration.

**Colin-XKL/FeedCraft** — craft your feed at ease! 轻量级rss中间件, 提取全文, 翻译、摘要一站式服务

- Repository: https://github.com/Colin-XKL/FeedCraft
- Website: https://feed-craft.colinx.one/start.html
- Stars: 311 · Forks: 11
- Language: Go
- License: GPL-3.0
- Published: 2026-09-15 · Updated: 2026-09-15 · Language: en
- Canonical page: https://hysenlabs.com/projects/colin-xkl-feedcraft

## Portable mode is a URL prefix, and that is the whole pitch

The calling convention is the first thing to understand, because it is not an API and there is nothing to integrate.

```text
https://feed-craft.colinx.one/craft/translate-title?input_url=https://feeds.feedburner.com/visualcapitalist
```

The path segment is the name of the processing step, and the query parameter is your original feed. So a feed you cannot skim quickly because it is entirely in English becomes a title-translated feed by changing its subscription address and nothing else.

The documentation calls this portable mode: bring it and go, adding a prefix to the original RSS address. The alternative is called dock mode, where the background page lets you define feed addresses and the depth of processing you want.

One practical note is easy to miss. Some RSS readers do not escape characters automatically, so a crafted URL containing punctuation can arrive broken. The project provides a tool in the console to do the escaping for you.

Two feeds are suggested for testing, and the pair is chosen to show both cases: a full-text English feed and an English feed without full text. The public demo instance is explicitly for trying things out rather than depending on.

## Three nouns: an AtomCraft, a FlowCraft, and a Recipe

The vocabulary is small and worth learning because the whole configuration model is built on it.

An AtomCraft is one processing step: how to handle one feed, whether that means translating it, extracting the full text, or generating an AI summary.

A FlowCraft is a sequence of AtomCrafts. The example given is defining one called clean-article that combines full-text extraction, AI article filtering, and AI summarisation into a single named unit.

A Recipe records which Craft or FlowCraft processes which feed. The example is a recipe named my-zhihu-daily that applies AI summarisation to a Zhihu daily feed, and the recipe produces a new RSS address you can subscribe to for a summarised version.

So the three levels map onto three questions: what is one operation, what is a pipeline, and what is a subscription.

That structure is why the tool does not need a plugin system. A new pipeline is a name and an ordered list, and a new subscription is a recipe pointing at one, which is a much smaller surface to maintain than user-supplied code.

## The AtomCraft list splits mechanical passes from AI passes

Eleven AtomCrafts are documented, and they fall into two groups by what they require.

The mechanical ones need no model. `proxy` is a simple RSS proxy that processes nothing. `limit` caps the number of articles, defaulting to the latest ten. `fulltext` fetches the article body. `fulltext-plus` does the same but simulates browser rendering, and it exists for sites where the normal mode cannot retrieve content because the page is rendered dynamically.

The AI ones call a model with a customisable prompt. `introduction` generates a summary attached to the start of the original text, and `summary` generates a summary of the main content, also attached at the start. `translate-title` translates only the title. `translate-content` translates the body and outputs only the translation. `translate-content-immersive` is the bilingual mode, appending the translation after each original paragraph instead of replacing it.

Two more are content shaping rather than translation: `beautify-content` tidies the layout and removes ads and irrelevant material, and `ignore-advertorial` filters out marketing articles.

The dynamic rendering path is the one with an external dependency, and it is why the configuration carries a browser provider setting with two options, a REST browser service or a Chrome DevTools Protocol endpoint.

## Default console credentials are published, and the readme says to change them

The self-hosting instructions are a compose file, and the security note is in the same breath.

The console account is `admin` with the password `adminadmin`, and the documentation asks that you change the default password as soon as you log in. Those credentials are in a public readme, which is the reason to treat them as a to-do rather than a default.

The minimal compose maps port 10088 on the host to 80 in the container, mounts a local directory at the container's database path, and restarts always.

```yaml
    image: ghcr.io/colin-xkl/feed-craft
    ports:
      - "10088:80"
```

Images are published on GitHub Container Registry and on Docker Hub, and a second example shows redis and the rendering service deployed in the same compose file rather than assumed to exist.

The environment variables are where the dependencies show. `FC_BROWSER_PROVIDER` and `FC_BROWSER_ENDPOINT` point at the rendering service, `FC_REDIS_URI` at a redis instance used as cache, and the LLM base URL has an explicit requirement that it end with `/v1`, alongside an API key for authentication.

So a working install is three services: the application, a cache, and a headless browser.

## Concurrent model calls are capped at three across all feeds

One setting in the environment template is easy to overlook and it governs cost.

`FC_LLM_MAX_CONCURRENCY` sets the global maximum number of concurrent LLM requests shared by all feeds and all features, and the default is 3.

That word global is the important part. A single crafted feed with twenty articles does not get twenty parallel summarisation calls; every feed and every feature draws from one budget of three. The comment in the file is explicit that it is shared rather than per feed.

The embedding path has its own limits and they are described with the reasoning attached. `FC_EMBEDDING_BATCH_SIZE` caps how many texts go to the embedding service in one request, defaulting to 5, with a note to adjust it for model size and hardware. `FC_EMBEDDING_MAX_INPUT_CHARS` caps the characters per text including any instruction prefix, defaulting to 8000.

The embedding configuration also has a fall-back rule worth knowing: if the embedding type, base, or key are not set, they fall back to the LLM settings above. The model name does not fall back, because the documentation is explicit that it must be an embedding model rather than a chat model, and setting it to the wrong kind of model produces a runtime failure rather than a warning.

There is also an optional render timeout for the browser provider.

## The example environment file still describes version 2.0

The environment template is dated in a way that tells you it is not regenerated per release.

The outbound user agent for feed requests is set to `FeedCraft/2.0`, and the default language for translation is `zh-CN`. The example LLM model is `gpt-3.5-turbo` with the type set to `openai`, and the default API base is the OpenAI endpoint.

Meanwhile the current release line is 3.x, with the newest tag at v3.2.0. So a fresh clone produces a user agent claiming a major version two releases behind, and a chat model name that is a generation or more old.

Neither is dangerous, and both are informative. The user agent is the string some feed servers see when FeedCraft fetches a feed, and if a site is blocking an old-looking client the version string is the first thing to bump. The default model is the setting most likely to produce a poor summary, and it is only a default.

The HTML user agent is a separate value and is a full Chrome string, which tells you the tool fetches pages pretending to be a browser as well as pretending to be FeedCraft when fetching feeds.

## The generator makes feeds out of pages, JSON, and searches

The read-only half of the product is often the useful half: turning something that is not a feed into one.

The built-in visual generator covers three inputs. HTML pages become feeds. JSON API responses become feeds, with support for importing the request as a cURL statement rather than making you reconstruct it by hand. And search results become feeds.

The Go dependencies explain how each is handled. JSON responses are processed with a JSON query library rather than being parsed ad hoc, which is what allows a specific field of an API response to become an item. The feed output itself is generated with a feed library, and incoming feeds are parsed with a widely used feed parser. Article body extraction uses a readability implementation, so full text extraction is a readability algorithm applied to the fetched HTML rather than a tag heuristic.

The console also has a comparison tool: give it a feed address, choose an AtomCraft, and see the before and after side by side. The documentation uses a custom craft that keeps only articles whose title contains a specific token as the demonstration of what filtering looks like.

Together with custom prompt creation and recipe assignment, that gives you a way to see an effect before subscribing to it.

## The Go module is named FeedCraft with a capital F

The module declaration is unusual for Go, and it is the kind of detail that matters if you import it.

The module path is `FeedCraft`, capitalised, with a capital F and a capital C, rather than a lowercase URL-shaped path. Go tooling is tolerant of this, but it means there is no repository URL in the module path and any import you write has to match it exactly.

The toolchain requirement is explicit: the language version is 1.25.0 with a pinned toolchain of 1.25.13.

The dependency list is a map of the architecture. Headless Chrome is driven by chromedp, which is the `fulltext-plus` path. Readability comes from a Go port of Shiori's implementation. Language detection is present, which is what a translate step needs to know the source language. The model calls go through langchaingo rather than a vendor SDK, which is what allows any OpenAI compatible endpoint to work. Storage is pure-Go SQLite with an ORM on top, caching is redis, and configuration is read through cobra and viper.

Two smaller dependencies are worth noting for operations: sentry is wired in for error reporting, and a library that automatically sets the maximum process count is imported, which is the kind of thing that stops a container from being throttled by a CPU limit it did not know about.

The repository is GPLv3, which matters if you intend to modify and serve it.

## Conclusion

FeedCraft fits someone with a pile of half-English feeds who wants translated and summarised versions without running a scraper per site, since the portable mode needs nothing but a changed subscription URL. It does not fit anyone hosting a public instance without changing the default console credentials, which are published in the readme. Before deploying, set the admin password, point `FC_BROWSER_ENDPOINT` at a real rendering service for the dynamic-page features, and note that the example environment file still carries a version 2.0 user agent string.

## FAQ

### What is feed?

In FeedCraft's terms a feed is a stream of items an RSS reader polls, and the tool treats it as input to a pipeline rather than as the end product. It can also create feeds from web pages, JSON API responses, and search results, so the direction of travel can be reversed.

### Which AtomCrafts does FeedCraft provide?

Eleven: proxy, limit, fulltext, fulltext-plus for dynamically rendered pages, introduction, summary, translate-title, translate-content, translate-content-immersive for bilingual output, beautify-content, and ignore-advertorial. Only the browser-rendering one needs an external rendering service.

### How do I call FeedCraft on a feed?

With a URL of the form https://feed-craft.colinx.one/craft/{craft_name_here}?input_url={input_rss_url}, where the path segment is the AtomCraft or FlowCraft to apply and the query parameter is your original feed. Some readers do not escape characters automatically, so the console includes a tool to encode the URL for you.

### What is the difference between portable mode and dock mode?

Portable mode requires nothing but a prefix on the original feed address, so it works with any existing reader immediately. Dock mode is the background console where you define feed addresses and the depth of processing you want, including custom prompts and recipe assignments.

### What does self-hosting FeedCraft require?

A container image plus three services: the application, a redis instance for FC_REDIS_URI, and a headless browser pointed at by FC_BROWSER_PROVIDER and FC_BROWSER_ENDPOINT, plus an LLM endpoint whose base URL ends with /v1. The console ships with the account admin and the password adminadmin, which the documentation asks you to change immediately.

## Sources

- [Colin-XKL/FeedCraft on GitHub](https://github.com/Colin-XKL/FeedCraft)
- [License: GPL-3.0](https://github.com/Colin-XKL/FeedCraft/blob/main/LICENSE)
- [Project website](https://feed-craft.colinx.one/start.html)
- [README](https://github.com/Colin-XKL/FeedCraft/blob/main/README.md)
- [Releases](https://github.com/Colin-XKL/FeedCraft/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/colin-xkl-feedcraft
