一个本地优先、面向个人使用的极简 RSS 阅读器。
它优先解决“顺手订阅、稳定刷新、舒服阅读、可离线查看、易于迁移”这些实际问题,而不是做成一个厚重的平台。
English README | 文档索引 | 贡献说明 | MIT License
RSS-Reader 是一个以 Rust 为核心、基于 Dioxus 构建的跨平台阅读器,当前重点覆盖:
- 桌面端日常阅读
- Web 端浏览器验证与静态部署
- Android Debug APK 构建与后续移动端演进
- 本地 SQLite 持久化
- JSON / OPML 配置交换
- 可选 WebDAV 配置同步
- CLI 自动化与自定义 CSS 主题
如果你需要的是这样一种工具,这个项目会比较合适:
- 不想把订阅数据交给第三方平台
- 希望桌面端优先、浏览器也能跑
- 需要离线阅读能力,而不只是在线看摘要
- 希望订阅配置能导出、迁移、脚本化
- 想自己写 CSS 主题,或者让 AI 帮你生成主题
- 添加 / 删除 RSS feed
- 刷新单个订阅或全部订阅
- 文章列表、阅读页、已读 / 收藏 / 搜索
- 阅读页支持:
- 返回上一页
- 上一篇未读 / 下一篇未读
- 上一篇同订阅文章 / 下一篇同订阅文章
- 桌面端使用本地 SQLite
- Web 使用浏览器本地持久化状态(当前实现为
localStorage序列化状态文件) - feed 提供多少正文,就缓存多少正文
- 桌面端 / Android 会在 feed 刷新完成后,尽量把正文中的图片资源在后台本地化进缓存 HTML
- Web 端正文也会缓存,但图片本地化受浏览器 CORS 约束
- 导入 / 导出配置包 JSON
- 导入 / 导出 OPML
- WebDAV 上传 / 下载配置
- 自定义 CSS 主题、主题卡片、预置主题切换
rssr-cli可用于:- 列出订阅
- 添加 / 删除 feed
- 刷新订阅
- 导入 / 导出配置
- 导入 / 导出 OPML
- 查看 / 保存设置
- 推送 / 拉取 WebDAV 配置
| 平台 | 当前状态 |
|---|---|
| Windows Desktop | 可发布 |
| Linux Desktop | 可发布 |
| macOS Desktop | 可发布 |
| Web | 可发布 |
| Android Debug APK | 已接入 |
| Android Signed Release APK / AAB | 已有 workflow,待 secrets 与正式验收 |
如果你只是想使用,不想自己编译,最简单的方式是直接下载 GitHub Release 附件。
当前会发布这些产物:
RSS-Reader-windows-x86_64.zipRSS-Reader-linux-x86_64.tar.gzRSS-Reader-macos-x86_64.tar.gzRSS-Reader-macos-aarch64.tar.gzRSS-Reader-web.tar.gzRSS-Reader-android-arm64-v8a-debug.apk
另外还会附带 CLI 和部分 Android release 产物:
rssr-cli-windows-x86_64.ziprssr-cli-linux-x86_64.tar.gzrssr-cli-macos-x86_64.tar.gzrssr-cli-macos-aarch64.tar.gzRSS-Reader-android-arm64-v8a-release.apkRSS-Reader-android-arm64-v8a-release.aab
说明:
- Android 默认发布的是适用于真机的
arm64-v8aDebug APK - Android release APK / AAB 只有在仓库配置签名 secrets 后才会一起出现
- 如果你本地自己执行
dx bundle --platform android ...,记得随后运行python3 scripts/prepare_android_bundle.py target/dx/rssr-app/release/android/app/app/src/main,再进入生成的 Gradle 工程执行一次./gradlew assembleDebug或assembleRelease,这样 Android 启动图标、应用显示名,以及targetSdk / compileSdk才会真正打进 APK / AAB,避免被系统提示“此应用是针对旧版安卓开发的” - Windows 桌面端通常需要系统已安装 WebView2 Runtime
如果你要改代码、调试、或自己出包,可以本地编译。
cargo run -p rssr-apprustup target add wasm32-unknown-unknown
cargo install dioxus-cli --version 0.7.3 --locked
dx serve --platform web --package rssr-app注意:
- Web 端远端 feed 是否能抓取,取决于目标站点是否允许跨域请求
- 有些 feed 在 desktop / Android 正常,在 Web 会被浏览器 CORS 限制拦住
- 如果你通过
rssr-web部署 Web 版本,服务端会代抓 feed,所以像https://www.ruanyifeng.com/blog/atom.xml这类会被浏览器 CORS 拦住的源也能正常订阅 - Web 端为避免浏览器缓存导致“刷新看起来没生效”,会在刷新 feed 时附加 cache-busting 参数
https://blogs.nvidia.com/feed/这类源在浏览器里通常可直接使用;https://www.ruanyifeng.com/blog/atom.xml这类源请优先通过rssr-web/ Docker 部署方式验证
cargo fmt --all
cargo test --workspace
cargo check -p rssr-app --target wasm32-unknown-unknown# 桌面端
cargo run -p rssr-app
# CLI
cargo run -p rssr-cli -- --help
# 仅检查 web target
cargo check -p rssr-app --target wasm32-unknown-unknown
# Android smoke check
cargo check -p rssr-app --target aarch64-linux-android打开“订阅”页,输入一个 RSS / Atom feed URL,然后点击“添加订阅”。
建议优先用桌面端测试真实远端 feed,因为:
- 桌面端不会受浏览器 CORS 限制
- Web 端只有目标站点允许跨域时才能直接刷新远端 feed
“文章”页支持:
- 标题搜索
- 仅未读
- 仅收藏
- 进入阅读页
阅读页支持:
- 返回上一页
- 标记已读 / 未读
- 收藏 / 取消收藏
- 上一篇未读 / 下一篇未读
- 上一篇同订阅文章 / 下一篇同订阅文章
- 快捷键
M切换已读,F切换收藏 - 快捷键
←跳到上一篇未读,→跳到下一篇未读
“设置”页支持:
- 直接编辑自定义 CSS
- 导入主题文件
- 导出当前 CSS
- 预置主题按钮
- 主题下拉
- 主题卡片切换
如果你想自定义外观但不想手写 CSS,可以先读:
当前支持:
- 配置包 JSON 导入 / 导出
- OPML 导入 / 导出
- WebDAV 配置同步
配置交换的原则是:
- 迁移订阅与设置
- 不把它做成一个云端数据库同步平台
- 本地阅读库仍然以本地为主
桌面端使用本地 SQLite。
默认数据库会自动创建在可执行文件同目录下的 RSS-Reader/rss-reader.db。首次启动时程序会自动:
- 创建数据目录
- 创建 SQLite 数据库
- 执行迁移
Web 端当前使用浏览器本地持久化状态,而不是和桌面端完全相同的 SQLite 文件实现。
当前实现特征:
- 状态序列化后持久化在浏览器本地存储中
- 刷新页面、重新打开浏览器标签页后仍会保留订阅、文章、已读、收藏和设置
- 这条实现路线优先保证当前 Web 版可用性与跨浏览器稳定性
当前缓存边界是:
- feed 提供多少正文,就缓存多少正文
- 不默认抓取站点原网页去补全文
另外:
- 桌面端 / Android 会在刷新成功后,于后台尽量把正文里的图片资源本地化进缓存 HTML
- 这样已经成功缓存过的图片,在远端删除后仍然可读
- 图片本地化不会再阻塞“新增订阅 / 刷新订阅”的主流程;即使图片抓取失败或超时,刷新本身仍然成功
- Web 端正文也会缓存,但图片本地化受浏览器 CORS 限制,可能保留远端 URL
当前 release workflow 会发布:
- Windows desktop
- Linux desktop
- macOS desktop
- Web 静态包
- Android debug APK
如果配置了 Android signing secrets,还会额外发布:
- Android release APK
- Android release AAB
仓库包含 Web 版本 的容器化部署支持。
当前镜像运行的是一个很薄的 rssr-web 服务进程,它负责:
- 显示用户名 / 密码登录页
- 在服务端校验凭据
- 签发
HttpOnly会话 cookie - 登录后再提供 Dioxus Web 静态资源与 SPA 路由回退
它 不是 桌面端、CLI、Android 或本地开发运行所需的依赖;只有 Docker / GHCR 的 Web 部署镜像才会用到它。
如果你本地开发或运行原生版本,完全不需要这层部署服务:
- 桌面端:
cargo run -p rssr-app - Web 开发:
dx serve --platform web --package rssr-app
如果你要验证 Web 部署态的完整能力,尤其是:
- 用户名 / 密码登录
- 同源
/feed-proxy - CORS 受限源(例如
https://www.ruanyifeng.com/blog/atom.xml)
推荐直接本地运行 rssr-web:
dx bundle --platform web --package rssr-app --release --debug-symbols false --out-dir target/web-e2e
cargo run -p rssr-web -- --print-password-hash adminadmin
RSS_READER_WEB_BIND=127.0.0.1:8060 \
RSS_READER_WEB_STATIC_DIR=target/web-e2e/public \
RSS_READER_WEB_USERNAME=admin \
RSS_READER_WEB_PASSWORD_HASH='<把上一步输出粘贴到这里>' \
RSS_READER_WEB_SESSION_SECRET=01234567890123456789012345678901 \
cargo run -p rssr-web然后访问 http://127.0.0.1:8060/login,用 admin / adminadmin 登录后测试 feed 导入与刷新。
rssr-web 的登录账号由环境变量控制,最常用的就是这三项:
RSS_READER_WEB_USERNAMERSS_READER_WEB_PASSWORD_HASHRSS_READER_WEB_SESSION_SECRET
推荐按下面的顺序手动修改:
- 先生成一个新的 Argon2 密码哈希:
cargo run -p rssr-web -- --print-password-hash '请换成你自己的强密码'- 然后在启动
rssr-web或docker compose之前,替换环境变量:
export RSS_READER_WEB_USERNAME='请换成你的用户名'
export RSS_READER_WEB_PASSWORD_HASH='把上一步输出的 Argon2 哈希粘贴到这里'
export RSS_READER_WEB_SESSION_SECRET='至少32字符的随机长串'- 重新启动服务:
cargo run -p rssr-web或者:
docker compose up -d补充说明:
RSS_READER_WEB_PASSWORD_HASH变了以后,旧密码会立即失效RSS_READER_WEB_USERNAME变了以后,登录页就必须使用新用户名RSS_READER_WEB_SESSION_SECRET变了以后,旧会话 cookie 会失效,用户需要重新登录- 部署环境不要再保存明文密码,优先只保留 Argon2 哈希
默认的 docker-compose.yml 是“直接拉取 GitHub Container Registry 镜像”的部署模板,不会在本地重新构建镜像。
如果你是从零开始在服务器上部署,最实用的方式是直接准备一个 .env 和一个 compose.yaml。
services:
rss-reader:
image: ghcr.io/develata/rss-reader:latest
environment:
RSS_READER_WEB_USERNAME: ${RSS_READER_WEB_USERNAME}
RSS_READER_WEB_PASSWORD_HASH: ${RSS_READER_WEB_PASSWORD_HASH:-}
RSS_READER_WEB_PASSWORD: ${RSS_READER_WEB_PASSWORD:-}
RSS_READER_WEB_SESSION_SECRET: ${RSS_READER_WEB_SESSION_SECRET:-}
RSS_READER_WEB_AUTH_STATE_FILE: ${RSS_READER_WEB_AUTH_STATE_FILE:-/app/auth/auth.json}
RSS_READER_WEB_ENV: ${RSS_READER_WEB_ENV:-development}
RSS_READER_WEB_SECURE_COOKIE: ${RSS_READER_WEB_SECURE_COOKIE:-false}
RSS_READER_WEB_TRUST_PROXY_HEADERS: ${RSS_READER_WEB_TRUST_PROXY_HEADERS:-false}
RSS_READER_WEB_SESSION_TTL_HOURS: ${RSS_READER_WEB_SESSION_TTL_HOURS:-12}
ports:
- "${RSS_READER_PORT:-8039}:8080"
volumes:
- rss-reader-auth:/app/auth
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "wget -q -O /dev/null http://127.0.0.1:8080/healthz || exit 1"]
interval: 30s
timeout: 5s
retries: 3
start_period: 10s
volumes:
rss-reader-auth:RSS_READER_WEB_USERNAME=admin
RSS_READER_WEB_PASSWORD=adminadmin
RSS_READER_WEB_AUTH_STATE_FILE=/app/auth/auth.json
RSS_READER_WEB_ENV=development
RSS_READER_WEB_SECURE_COOKIE=false
RSS_READER_WEB_TRUST_PROXY_HEADERS=false
RSS_READER_PORT=8039准备好这两个文件后,直接启动:
docker compose up -d首次启动时,如果你只提供:
RSS_READER_WEB_USERNAMERSS_READER_WEB_PASSWORD
程序会自动做两件事,并写入 RSS_READER_WEB_AUTH_STATE_FILE:
- 生成 Argon2 密码哈希
- 生成随机
session secret
后续只要这个认证状态文件还在,单纯重启 rssr-web / Docker 不会触发“首次登录重新设置账号密码”。
重启只会重新读取环境变量,并继续使用已经持久化的哈希与 secret。
在 Unix/Linux 环境下,认证状态文件会以仅当前用户可读写的权限写入。
默认访问:
http://127.0.0.1:8039
rssr-web 常用环境变量可以分成三类:
- 必填:
RSS_READER_WEB_USERNAME- Web 登录用户名
- 二选一:
RSS_READER_WEB_PASSWORD_HASH- Argon2 密码哈希
- 推荐正式部署使用
RSS_READER_WEB_PASSWORD- 明文密码
- 可用于首次启动时自动生成哈希并持久化
- 可选:
RSS_READER_WEB_AUTH_STATE_FILE- 持久化保存 Argon2 哈希和 session secret 的认证状态文件路径
- 默认是当前用户主目录下的
.rssr-web-auth.json
RSS_READER_WEB_SESSION_SECRET- 用来签发和校验会话 cookie 的密钥
- 如果不填,程序会在首次启动时自动生成并写入
RSS_READER_WEB_AUTH_STATE_FILE - 如果显式填写,长度至少 32 个字符
RSS_READER_WEB_ENVdevelopment或production- 默认
development
RSS_READER_WEB_SECURE_COOKIEtrue时 cookie 只通过 HTTPS 发送- 默认
false
RSS_READER_WEB_TRUST_PROXY_HEADERS- 是否信任
X-Forwarded-For/X-Real-IP - 默认
false - 只有在
rssr-web明确部署在你自己的反向代理后面时才建议开启
- 是否信任
RSS_READER_WEB_SESSION_TTL_HOURS- 登录会话有效期,单位小时
- 默认
12
RSS_READER_PORT- Docker 对外暴露端口
- 默认
8039
RSS_READER_IMAGE- 自定义镜像名
- 默认
ghcr.io/develata/rss-reader:latest
额外说明:
RSS_READER_WEB_PASSWORD_HASH和RSS_READER_WEB_PASSWORD是二选一- 如果两者都不填,但认证状态文件里已经有哈希,程序会继续使用那个已持久化哈希
RSS_READER_WEB_SESSION_SECRET如果不填,程序会优先复用认证状态文件里的 secret;只有首次缺失时才会自动生成- 程序会把生成结果写入认证状态文件,但不会自动回填到
.env、compose.yaml或容器平台变量面板
如果你只是想尽快在本地跑起来,可以直接使用明文密码:
services:
rss-reader:
image: ghcr.io/develata/rss-reader:latest
environment:
RSS_READER_WEB_USERNAME: admin
RSS_READER_WEB_PASSWORD: adminadmin
RSS_READER_WEB_AUTH_STATE_FILE: /app/auth/auth.json
RSS_READER_WEB_ENV: development
RSS_READER_WEB_SECURE_COOKIE: "false"
RSS_READER_WEB_TRUST_PROXY_HEADERS: "false"
RSS_READER_WEB_SESSION_TTL_HOURS: "12"
ports:
- "8039:8080"
volumes:
- rss-reader-auth:/app/auth
restart: unless-stopped
volumes:
rss-reader-auth:说明:
- 这种方式只适合本地开发、临时测试、局域网自用
- 首次启动时,程序会自动生成 Argon2 哈希并写入
RSS_READER_WEB_AUTH_STATE_FILE - 首次启动时,如果你没有填写
RSS_READER_WEB_SESSION_SECRET,程序也会自动生成一个随机 secret 并写入同一个状态文件 - 之后即使移除
RSS_READER_WEB_PASSWORD,只要认证状态文件和卷还在,仍然可以继续登录 - 后续重启容器不会再次进入“首次设置”流程;除非你删除认证状态文件或显式更换用户名/密码/secret
- 更稳的做法是:
- 首次启动成功后,删除明文
RSS_READER_WEB_PASSWORD - 只保留认证状态文件或改成显式
RSS_READER_WEB_PASSWORD_HASH
- 首次启动成功后,删除明文
如果你要正式部署到服务器,推荐只使用 Argon2 哈希:
services:
rss-reader:
image: ghcr.io/develata/rss-reader:latest
environment:
RSS_READER_WEB_USERNAME: admin
RSS_READER_WEB_PASSWORD_HASH: "$argon2id$..."
RSS_READER_WEB_AUTH_STATE_FILE: "/app/auth/auth.json"
RSS_READER_WEB_ENV: "production"
RSS_READER_WEB_SECURE_COOKIE: "true"
RSS_READER_WEB_TRUST_PROXY_HEADERS: "true"
RSS_READER_WEB_SESSION_TTL_HOURS: "12"
ports:
- "8039:8080"
volumes:
- rss-reader-auth:/app/auth
restart: unless-stopped
volumes:
rss-reader-auth:如果你只想直接填密码,然后让程序自动生成哈希,需要注意:
- 当前
rssr-web已支持“首次启动时自动把明文密码转成 Argon2 哈希,并写入认证状态文件” RSS_READER_WEB_SESSION_SECRET也支持首次自动生成并写入认证状态文件- 但它不会自动把哈希回填到
.env、compose.yaml或容器平台变量面板 - 如果你想把配置彻底切换到显式哈希,仍然可以手动生成:
cargo run -p rssr-web -- --print-password-hash '请换成你自己的强密码'- 然后把输出粘贴到
RSS_READER_WEB_PASSWORD_HASH
原因很简单:
- 服务可以生成哈希
- 也可以生成 session secret
- 但它并不知道你希望把这些值写回哪里
.envcompose.yaml- CI/CD secret
- 容器平台变量面板
所以现在的设计是:
- 本地开发:可以直接用
RSS_READER_WEB_PASSWORD,程序会自动生成并持久化哈希 - 本地开发:如果不填
RSS_READER_WEB_SESSION_SECRET,程序也会自动生成并持久化 - 正式部署:
- 最稳的是直接使用
RSS_READER_WEB_PASSWORD_HASH - 最稳的 session secret 方式仍然是由你自己提供一个高熵随机值
- 也可以先用
RSS_READER_WEB_PASSWORD启动一次,让程序生成并持久化哈希,再移除明文密码 - 只有在服务明确运行在你控制的反向代理后面时,才建议设置
RSS_READER_WEB_TRUST_PROXY_HEADERS=true
- 最稳的是直接使用
如果你想手动轮换登录配置:
- 改用户名:
- 修改
RSS_READER_WEB_USERNAME - 重启服务
- 修改
- 改密码:
- 重新填写
RSS_READER_WEB_PASSWORD - 或生成新的
RSS_READER_WEB_PASSWORD_HASH - 重启服务
- 重新填写
- 让所有旧登录态失效:
- 修改
RSS_READER_WEB_SESSION_SECRET - 或删除
RSS_READER_WEB_AUTH_STATE_FILE - 重启服务
- 修改
也支持通过环境变量覆盖镜像名和端口:
RSS_READER_WEB_USERNAME=admin \
RSS_READER_WEB_PASSWORD_HASH='请替换成 Argon2 密码哈希' \
RSS_READER_WEB_SESSION_SECRET='至少32字符的随机长串' \
RSS_READER_IMAGE=ghcr.io/develata/rss-reader:latest \
RSS_READER_PORT=8090 \
docker compose up -d如果你不想用 compose,也可以直接运行镜像:
docker run --rm \
-p 8039:8080 \
-e RSS_READER_WEB_USERNAME=admin \
-e RSS_READER_WEB_PASSWORD_HASH='请替换成 Argon2 密码哈希' \
-e RSS_READER_WEB_SESSION_SECRET='至少32字符的随机长串' \
ghcr.io/develata/rss-reader:latest说明:
- 推荐先生成密码哈希:
cargo run -p rssr-web -- --print-password-hash '请改成你自己的强密码'- 部署环境请使用
RSS_READER_WEB_PASSWORD_HASH,不要继续保存明文密码 RSS_READER_WEB_SESSION_SECRET请使用长度至少 32 的随机字符串RSS_READER_WEB_TRUST_PROXY_HEADERS默认应保持false- 只有当
rssr-web部署在你自己可控的反向代理后面时,才建议设为true - 生产环境建议同时设置:
RSS_READER_WEB_ENV=productionRSS_READER_WEB_SECURE_COOKIE=true
- 如果启用了反向代理并希望限速按真实客户端 IP 生效,可再设置:
RSS_READER_WEB_TRUST_PROXY_HEADERS=true
- 如果启用了
RSS_READER_WEB_ENV=production,但没有开启RSS_READER_WEB_SECURE_COOKIE=true,服务会拒绝启动 - 本地 HTTP 测试时可保持
RSS_READER_WEB_ENV=development
docker compose -f docker-compose.yml -f docker-compose.build.yml up --build这会在保留 compose 默认端口配置的同时,覆盖成“从当前工作区构建镜像”。
如果你只想手动验证 Dockerfile,也可以直接:
docker build -t rss-reader-web .容器内会带基础健康检查,适合本地部署和简单服务器场景。
推荐使用 Docker 的场景:
- 想部署带登录门禁的 Web 版本到服务器
- 想快速拉起一个受保护的浏览器入口
- 不想安装 Rust / Dioxus CLI
不推荐使用 Docker 的场景:
- 想用桌面端离线阅读体验
- 想直接测试 Windows / Linux / macOS 原生二进制
- 想调试桌面端系统行为
更完整的说明放在 docs/:
crates/
├── rssr-app/
├── rssr-cli/
├── rssr-web/
├── rssr-application/
├── rssr-domain/
└── rssr-infra/
assets/
docs/
migrations/
specs/
tests/
因为浏览器会受 CORS 限制。 很多 feed 在桌面端能正常抓取,但在 Web 端会被目标站点阻止跨域请求。
因为桌面端 / Android 对正文图片本地化更完整;Web 端会受浏览器安全策略限制。
CLI 主要给自动化、脚本和高级用户使用。
普通用户只下载 rssr-app 即可。
通常需要系统具备 Microsoft WebView2 Runtime。很多 Windows 10/11 机器已经预装。
本项目使用 MIT License。