Rumi runs without a Meta account now
Until today, trying Rumi meant getting a WhatsApp Business account approved by Meta first. That's gone. The messaging channel is pluggable, and the default links your own WhatsApp with a QR code the way WhatsApp Web does.
A clone now goes from git clone to a real conversation in about fifteen minutes.
./install.sh # tools, dependencies, the `rumi` command
rumi setup # five guided steps
rumi startThen message the number it linked and send Hi.
When you're ready for a real deployment, rumi graduate moves you to an official WhatsApp Business number. Every teacher, conversation and past assessment carries over — Rumi identifies people by phone number, not by channel.
You'll need a second phone number to test from. Rumi answers as your number, so messaging it from the same account looks exactly like a broken bot. A spare SIM, an old phone, or a colleague's phone all work.
Setup is a wizard now, not a document
rumi setup asks in plain language — "where should Rumi keep its memory", never SUPABASE_URL. It checks every value against the real service as you type it, so a wrong paste is caught in the second it happens rather than surfacing hours later inside a feature. It writes each answer to .env immediately, so Ctrl+C costs nothing. And it skips whatever already works, so a re-run takes seconds.
It also catches the mistakes that are expensive precisely because the value looks fine — Supabase's anon key pasted where service_role belongs (both are eyJ… JWTs on the same page; the anon key can't see past row-level security, so the bot starts cleanly and finds no data), a phone number in Meta's PHONE_NUMBER_ID, another vendor's sk-… in your OpenRouter field.
Six commands: rumi setup, start, status, doctor, pair, graduate.
Meta-only features degrade instead of disappearing
A WhatsApp Flow is only a renderer — the logic lives in the endpoint. So on a channel with no Flows, Rumi drives those same endpoints as an ordinary chat conversation. /settings, /video, reading assessments and class setup all work; they just look plainer. Nothing is blocked.
⚠️ Breaking changes
Three, all small:
npm run setupnow launches the wizard. It used to run the preflight — that'snpm run doctor, unchanged..envis read from the repo root, not your working directory. If you keep abot/.env, move it up one level. Railway is unaffected. This fixed a real failure:cd bot && npm startloaded zero variables and aborted with "Missing REQUIRED env var(s)" on a fully configured deployment.REQUIRED_VARSis core-only, with each channel's own variables alongside it. Existing Meta deployments need no change — the driver is inferred asmetawhen your four Meta variables are present.
Fixes worth knowing about
Most of these were already broken on every deployment, including Meta, and were invisible because each failed inside a try/catch that made it look transient:
redisService.setNXandsetexWithCeilingnever existed — no quiz could ever be delivered, and every image message failed.quiz_class_*replies had no handler, so picking a class after/quizdid nothing.doctorshowed a green tick for an OpenRouter key with no credit — the worst kind of preflight, because it sends you looking for a bug in the bot. It reports your balance now.- Feature-intro videos produced unfetchable URLs when no asset host was configured, so Rumi asked "want to see how? 🎥", the teacher said yes, and nothing ever arrived.
- Reading assessments left an
.oggof a child's voice and a report PDF on disk forever. - A failure message claimed "our team has been notified". Nobody had been.
Four now have conformance guards so they can't come back silently.
Full changelog: CHANGELOG.md · Setup guide: SETUP.md · 170 test suites / 1997 tests