Build1 publisher3 min readPublished
The 43MB Secret: WhatsApp Automation Fails At Deploy, Not At The Model
A one-German-word-a-day channel took a week, three hosting platforms and a git history rewrite. None of the hard failures were in the AI layer.
The Engineer · Build desk
Drafted by a language model from the sources cited here and checked against its claim ledger before publication. How we use AISend a correction
What happened
- The author (dev.to, btkcodedev) set out to build a WhatsApp Channel that posts one German word every morning at A1 level, with an example sentence and a mnemonic.
- The project took a week and required a git history rewrite.
- Gemini can write the daily word, example sentence and mnemonic in a second; the hard part was never the AI and never the German.
- The project became an argument with WhatsApp's session model, two hosting platforms, and the author's own repo git history.
- whatsapp-web.js drives a real headless Chrome instance that logs into web.whatsapp.com like a browser would.
Compiled by The EngineerSomething wrong?How this is made
Why it matters
A developer writing as btkcodedev published a postmortem of building something deliberately small: a WhatsApp Channel that posts one A1-level German word each morning with an example sentence and a mnemonic [1]. The content generation was a non-event, since Gemini produces that in a second [3]; the week went to WhatsApp's session model, two hosting platforms, and the repo's own history [2][4].
The pivot point is what `whatsapp-web.js` actually is. It drives a real headless Chrome instance that logs into web.whatsapp.com the way a browser does [5], and it works locally [6]. Its persisted auth state is therefore a full Chromium profile: cookies, IndexedDB, cache [7]. His measured 43MB [8]. GitHub Secrets cap out in the kilobytes [9]. That is three to four orders of magnitude of mismatch [4], and base64 makes it worse, not better, because encoding a 43MB tar produces roughly 57MB of text [1].
Which is why the standard tutorial answer exists: tar the profile, base64 it, paste it into a secret anyway, decode it back into place on every CI run [10]. He shipped that, complete with a `setup.ts` whose entire job was printing a giant blob for manual pasting into GitHub's secret editor [11]. It does not fix the failure it appears to fix, because the session expires on WhatsApp's schedule rather than yours, and a phone offline too long kills it [12], which puts you back at a QR code on a server with no screen [13]. According to the author, two things work: run the linking step once locally against the same database the deployed job reads [14], or watch the CI provider's live log stream, where the QR prints as ASCII art you can scan off a terminal [15].
The platform layer failed independently. He had it deployed on Railway, Render and GitHub at once, and none of them worked [16]. Railway's fair-use policy explicitly bans userbots, meaning anything logging into a personal account over an unofficial reverse-engineered protocol instead of a real bot API [17], which is a precise description of `whatsapp-web.js` [18] and not a defect he could patch [19]. On Render he hit `RemoteAuth` with a Mongo-backed store, which has open, unresolved GitHub issues where the session saves but does not reliably restore, silently demanding a fresh QR scan on restart [20].
What stuck was changing libraries. Baileys speaks WhatsApp's multi-device protocol directly over a WebSocket, with no Chromium and no Puppeteer [21], and its session is a handful of small JSON credential files measured in kilobytes [22]. That fits in a free MongoDB Atlas cluster rather than a secret [23], and with no browser to start, it runs on a constrained free tier instead of choking on headless-Chrome memory pressure [24]. The real requirement was about sixty seconds of compute once per 24 hours [25], a 0.07 percent duty cycle [2], so the answer was a GitHub Actions cron job [26]. Dockerfile, Render and Railway all deleted; the deployment surface became one YAML file [27].
The bill came due in git. He had committed a full `.wwebjs_auth` folder, 204 files, plus standalone tar and base64 dumps at 27MB and 41MB [28]. That folder is a live login session, readable by anyone who could read the repo [29], and deleting it from the working tree leaves it in every earlier commit [30]. `git filter-repo` plus a force-push took `.git` from 84MB to 284KB [31], a roughly 300-fold shrink [3].
Worth watching if you are building this: check your host's acceptable-use policy for userbot language before writing code [17], and treat any credential that will not fit in your secrets manager as evidence the architecture is wrong, not as an encoding problem [10].