{"id":51035389,"url":"https://github.com/iuhoay/drift","last_synced_at":"2026-06-22T05:02:35.658Z","repository":{"id":364212851,"uuid":"1251907158","full_name":"iuhoay/drift","owner":"iuhoay","description":"yet another RSS reader","archived":false,"fork":false,"pushed_at":"2026-06-12T04:33:19.000Z","size":237,"stargazers_count":0,"open_issues_count":9,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-12T06:21:22.825Z","etag":null,"topics":["rails","rss"],"latest_commit_sha":null,"homepage":"https://rdrift.app/","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/iuhoay.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-28T02:38:03.000Z","updated_at":"2026-06-12T04:34:05.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/iuhoay/drift","commit_stats":null,"previous_names":["iuhoay/drift"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/iuhoay/drift","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iuhoay%2Fdrift","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iuhoay%2Fdrift/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iuhoay%2Fdrift/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iuhoay%2Fdrift/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/iuhoay","download_url":"https://codeload.github.com/iuhoay/drift/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iuhoay%2Fdrift/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34635038,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-22T02:00:06.391Z","response_time":106,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["rails","rss"],"created_at":"2026-06-22T05:02:34.762Z","updated_at":"2026-06-22T05:02:35.652Z","avatar_url":"https://github.com/iuhoay.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Drift\n\nA calm, fast, distraction-free RSS reader. Server-rendered Rails 8 + Hotwire,\nno SPA, no AI summaries, no social features — just feeds.\n\n## Stack\n\n- Ruby 4.0 / Rails 8 (main)\n- PostgreSQL (full-text search via `tsvector`)\n- Hotwire (Turbo + Stimulus)\n- Tailwind CSS v4\n- Solid Queue (background jobs + recurring scheduler)\n- Feedjira (parsing) + Faraday (HTTP, ETag/If-Modified-Since/redirects)\n\n## Authentication\n\nBuilt on the Rails 8 `bin/rails generate authentication` flow:\n\n- `User` with `has_secure_password` and `email_address`\n- `Session` records (DB-backed) keyed by a signed httponly cookie\n- `Authentication` controller concern with `before_action :require_authentication`\n  and `allow_unauthenticated_access` for the sign-in / sign-up / password-reset paths\n- `Current.user` / `Current.session` for thread-local access\n- Generator-shipped password reset emails (`PasswordsController`)\n- Drift adds a thin `RegistrationsController` on top for sign-up\n\n## Models\n\n- `User` — `email_address` + `has_secure_password`\n- `Session` — per-device login record (Rails 8 generator)\n- `Feed` — feed URL, ETag/Last-Modified, last-fetch metadata\n- `Subscription` — joins user ↔ feed (custom title)\n- `Entry` — feed item with FTS `search_vector`\n- `UserEntry` — per-user `read_at` / `starred_at`\n\n## Run it\n\n```bash\n# 1. Start Postgres (Docker is the easiest path):\ndocker compose up -d postgres\n\n# 2. Copy .env.example to .env and fill in your local Postgres creds.\ncp .env.example .env\n\n# 3. Install gems and bootstrap the DB:\nbundle install\nbin/rails db:create db:migrate db:seed\n\n# 4. Start the app + Tailwind watcher + Solid Queue worker:\nbin/dev\n```\n\nOpen http://localhost:3000. Seeded login: `demo@drift.local` / `drift1234`.\n\n### Environment variables\n\nDrift uses Rails 8.2's built-in `.env` support (no `dotenv-rails` gem). Values\nin `.env` are read via `Rails.application.dotenvs` and combined into\n`Rails.app.creds` alongside real `ENV` and `config/credentials.yml.enc`. The\n`.env` file is **not** loaded into `ENV` automatically — config files look up\nvalues explicitly:\n\n```erb\nusername: \u003c%%= Rails.application.creds.option(:database_username) %\u003e\n```\n\nIn production, set the same keys as real environment variables (they take\nprecedence over `.env`).\n\n### Production domain\n\nSet `APP_HOST` to the canonical public domain before booting production. Rails\nuses it for absolute URLs, including password reset emails, and Kamal uses it\nfor the proxy host.\n\n```bash\nAPP_HOST=rdrift.app\nAPP_HOSTS=rdrift.app,www.rdrift.app # optional aliases\nAPP_PROTOCOL=https\n```\n\nIf Kamal terminates TLS through its proxy, keep the default `FORCE_SSL=true` and\npoint DNS at the server before deploying so Let's Encrypt can issue the\ncertificate. For local production-style Docker compose, `FORCE_SSL=false` is set\nwith `APP_HOST=drift.local`.\n\n### Sending email\n\nDrift sends one transactional email — the password-reset link — delivered\nasynchronously through Solid Queue. Production reads SMTP settings entirely from\nthe environment (on ONCE these come from the host's mail settings UI):\n\n```bash\nSMTP_ADDRESS=smtp.example.com      # required to enable delivery\nSMTP_PORT=587                      # optional, defaults to 587\nSMTP_USERNAME=...                  # optional\nSMTP_PASSWORD=...                  # optional\nSMTP_AUTHENTICATION=plain          # optional, defaults to plain\nMAILER_FROM_ADDRESS=\"Drift \u003cno-reply@example.com\u003e\"\n```\n\nUntil `SMTP_ADDRESS` is set, mail delivery stays **off** — the app boots fine and\nqueued reset emails are dropped rather than retried forever. Bring any SMTP\nprovider (Postmark, SES, Resend, Fastmail, …); no provider gem is required.\n\n## Background jobs\n\nSolid Queue runs in-process via `bin/jobs` (started by `bin/dev`).\nA recurring `RefreshDueFeedsJob` enqueues a `FeedRefreshJob` for each feed\nthat hasn't been fetched in the last 30 minutes (see `config/recurring.yml`).\n\nTo refresh a single feed manually:\n\n```ruby\nFeedRefreshJob.perform_now(Feed.first.id)\n```\n\n## Bilibili feeds\n\nPasting a `space.bilibili.com/\u003cuid\u003e` address subscribes to that user's video\nuploads. Intended for personal use and **off by default in production** — see\n[docs/bilibili-feeds.md](docs/bilibili-feeds.md).\n\n## Search\n\nFull-text search uses Postgres `tsvector` with `websearch_to_tsquery`,\nranking title (A) \u003e summary (B) \u003e content (C) \u003e author (D). The vector is\nmaintained in the `Entry` model's `before_save`.\n\n## Tests\n\n```bash\nbin/rails test\n```\n\n## Backups\n\nProduction `drift_production` is dumped every few hours and pushed off-server to\nS3-compatible object storage (R2 / B2) by a Kamal **backup accessory** — a\nsidecar built from official `postgres:16` + AWS CLI. Tooling lives in\n[`backup/`](backup); setup, retention, and restore drills are in\n[docs/backups.md](docs/backups.md).\n\n## License\n\nDrift is free software, licensed under the **GNU Affero General Public License\nv3.0** (AGPL-3.0). See [LICENSE](LICENSE). In short: you may use, modify, and\nself-host it, but if you run a modified version as a network service, you must\nmake your source available to its users under the same license.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fiuhoay%2Fdrift","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fiuhoay%2Fdrift","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fiuhoay%2Fdrift/lists"}