feedbai hub
Runs feedbai for every project that installed the feedbai GitHub App, so projects need no bot account, no secrets and no machine of their own.
Hosted at https://feedbai.internet4000.com (Cloudflare Worker, every 10 minutes).
Invite-only for now. The hosted hub serves only the projects listed in FEEDBAI_PROJECTS (wrangler.jsonc): today feedbai and enform. Want yours added? Ask in #feedbai. Everything else stays open to everyone: the CLI with your own accounts, the Feedback button, feedbai send, and running your own hub.
The hub is a convenience, not a requirement: it keeps no data that matters, anyone can run one (or do without: on your own), and when it’s down the CLI says so and shows how to read with your own account (feedbai login …), or file from your CI (feedback → issues). An hourly check in .github/workflows/hub-health.yml opens an issue “feedbai hub is down” when it stops answering, and closes it when it’s back.
What it does per channel:
| Channel | Read with feedbai, no account |
Filed as feedback issues |
|---|---|---|
| Matrix room | public rooms the bot @feedbai:matrix.org was invited to, whose history anyone may read (“world_readable”) |
threads that mention the bot, in public rooms |
| Bluesky search | yes, with the hub’s account (feedbai.internet4000.com) |
no: sentiment, read only |
| Bluesky thread, author | yes (public, no account needed anyway) | thread: yes; author: no |
| Bluesky mentions | no: they are per account, so a repo can’t use the hub’s | no |
New channels (GitHub Discussions, Fediverse, …) are added when a project needs one: #10.
For a project: commit feedbai.config.json with your channels (npx @internet4000/feedbai finds them in the repo and prints the feedbai add … commands), invite the bot to your Matrix room, and install the App on your repo to get issues within ~10 minutes.
For people and agents without accounts: the CLI reads through the hub’s read API when no account of its own is configured. GET /v1/matrix?room=#room:server[&since=ISO][&limit=n] (only rooms readable without joining: “world_readable”) and GET /v1/bluesky/search?query=… return the same items as feedbai fetch --format json (cached 60s). Bluesky mentions sources are dropped from repo configs in the hub: they would read the hub’s own mentions.
Rules (any repo can install the App, so its config is untrusted):
FEEDBAI_PROJECTS(comma-separatedowner/name; unset: open to all) limits the hub to those projects: the scheduled run and the home page skip other repos even if they installed the App; the bot joins, and stays in, only public rooms whose topic names a listed repository (github.com/owner/name), and says why when it declines or leaves; the read API serves only those rooms; Bluesky search and every GitHub Actions request (OIDC) are for listed repositories only. Adding a project is a reviewed change to that line, and a deploy- the hub always reads with its own bot account;
accountsandjoinin a repo’s config are ignored - the bot joins public rooms only: it declines invites to others and leaves rooms that stop being public, so its shared token can’t expose private conversations. Private rooms: your own bot (on your own); with it, a private room is filed only if its topic names the repo (
github.com/owner/repo) - the read API is rate-limited per client (30 a minute) and only serves rooms readable without joining, or, to a GitHub Actions run proving its repository (OIDC), the rooms whose topic names that repository;
POST /v1/matrix/reactadds ✅ for it there (a project’s agent, with no secrets: an agent in the loop) GET /v1/statussays whether each hub secret still works; the hourly check opens an issue when one is rejected. Rotation: SECURITY.md- the bot joins rooms only when invited; kick it to stop
- GitHub access is the App’s installation token (one hour, minted each run, never stored): Issues: write and Single file: read on
feedbai.config.json. It never reads your code, public or private; you choose which repos it covers when installing, and uninstalling cuts it off - when a feedback issue is fixed (closed by a pull request or commit, or labelled
fixed), the bot replies “🤖 This is fixed” with its link, once, in the thread it came from (closing the loop;"replies": falsein the config stops it)
Personal data, retention and removal requests: PRIVACY.md. Security model: SECURITY.md.
Run your own (free software): create a bot account and a GitHub App (Issues: read & write, Single file: read on feedbai.config.json), then deploy the Cloudflare Worker (below), or run node hub/run.js from any cron with FEEDBAI_APP_ID, FEEDBAI_APP_PRIVATE_KEY, FEEDBAI_MATRIX_TOKEN and FEEDBAI_MATRIX_USER in its environment. No database: GitHub issues and their hidden markers are the state.
Cloudflare Workers (worker.js, wrangler.jsonc at the repo root; change the domain and vars for your own). From the repo root:
npx wrangler@4 deploynode hub/setup.js --url https://your-hub.example --owner <github org>npx wrangler@4 tail # watch the next scheduled runwrangler deploy first builds the website in site/ (Node ≥ 22.23.4, as Astro requires) and serves it from the same Worker. hub/setup.js serves a local page that creates the GitHub App (manifest flow) and logs the bot in; the App key and bot token go straight into the Worker’s secrets, never to disk. Connecting the repo in Cloudflare (Workers Builds: the Worker → Settings → Builds) deploys every push to main: empty build command, deploy command npx wrangler deploy, and the build variable NODE_VERSION=22.23.4 (Astro’s minimum).
Run only one hub per bot.
Other runtimes: the hub is one function, runHub({ env }) in hub/run.js, using only fetch and node:crypto. Call it from any scheduled handler (e.g. AWS Lambda + EventBridge).