You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Running WaSP in production — we have ~15 upstreamable commits, how would you like them? #8
We build Biznder, an AI-agents platform for local businesses, and WhatsApp is our main customer channel. A few weeks ago we replaced the session engine of our multi-tenant WhatsApp sidecar with WaSP — the layered credential storage and the anti-ban queue were exactly the pieces we didn't want to keep hand-rolling on top of raw Baileys. It now runs behind a contract-test suite that drives both our old engine and WaSP through the same scenarios, so WaSP's provider got exercised pretty hard in the process.
Along the way we accumulated a fork with ~15 commits we believe belong upstream: Biznder/wasp#feat/biznder-ports. Every commit is self-contained, in your code style, with vitest coverage. Rather than carpet-bombing you with PRs, here's the inventory — tell us what you want and in what shape, and we'll feed them in whatever granularity works for you:
Fixes
main currently doesn't compile (phantom re-exports in index.ts) and CI has been red since June — just opened fix: repair the build, make lint pass, de-flake CI #7 alongside this issue, it also fixes the 3 lint errors and a flaky uptime assertion.
Layered auth can't read (or write) Baileys' on-disk state: JSON.stringify applies Buffer#toJSON before a v instanceof Buffer replacer ever runs, so credentials round-trip as number arrays Baileys' reviver doesn't decode → selectBestCreds scores them invalid → silent re-pair of every migrated session. Plus the Postgres layer queries tables no code creates, and signal-key filenames aren't sanitized the way Baileys does it. Three commits, backward-compatible with the old encoding.
disconnect() leaves the WebSocket (and its keepalive) alive when logout() rejects — the common case, since you usually disconnect because the network is bad.
Media messages with no caption travel with caption: "" attached.
Reaction messages are silently dropped by the normalizer (no raw-message either).
Features
LID identity resolution: formatJid() always addresses <digits>@s.whatsapp.net, which WhatsApp answers with ack 463 for LID-only contacts and which forks the conversation in two. We route outbound sends through signalRepository.lidMapping, return the actually-addressed JID as Message.resolvedJid, and expose getPNForLID()/getLIDForPN() for raw-message consumers. Complements the TC-token work (which expects LIDs anyway).
Pairing-code linking mode ("link with phone number") with rotation + a 15s cooldown — currently WaSP is QR-only.
close() — stop a session without unlinking the device. Right now the only teardown is disconnect() → logout(), so a rolling deploy or a graceful SIGTERM unlinks every session from its phone.
QR TTL metadata (60s first QR, 20s refreshes) so consumers can render an accurate expiry countdown.
Opt-in bound on reconnects for sessions that never connected — dead creds looping forever is a reconnect storm WhatsApp punishes.
Testability seams: socketFactory, authStateFactory and a version option — this is what let us write 60+ provider tests with a hand-driven fake instead of module mocks; the fake ships with the first feature PR.
We use all of this in production, and we're happy to keep maintaining what lands (Discord too, if that's easier for coordination).
Hi Kobus,
We build Biznder, an AI-agents platform for local businesses, and WhatsApp is our main customer channel. A few weeks ago we replaced the session engine of our multi-tenant WhatsApp sidecar with WaSP — the layered credential storage and the anti-ban queue were exactly the pieces we didn't want to keep hand-rolling on top of raw Baileys. It now runs behind a contract-test suite that drives both our old engine and WaSP through the same scenarios, so WaSP's provider got exercised pretty hard in the process.
Along the way we accumulated a fork with ~15 commits we believe belong upstream:
Biznder/wasp#feat/biznder-ports. Every commit is self-contained, in your code style, with vitest coverage. Rather than carpet-bombing you with PRs, here's the inventory — tell us what you want and in what shape, and we'll feed them in whatever granularity works for you:Fixes
maincurrently doesn't compile (phantom re-exports inindex.ts) and CI has been red since June — just opened fix: repair the build, make lint pass, de-flake CI #7 alongside this issue, it also fixes the 3 lint errors and a flaky uptime assertion.JSON.stringifyappliesBuffer#toJSONbefore av instanceof Bufferreplacer ever runs, so credentials round-trip as number arrays Baileys' reviver doesn't decode →selectBestCredsscores them invalid → silent re-pair of every migrated session. Plus the Postgres layer queries tables no code creates, and signal-key filenames aren't sanitized the way Baileys does it. Three commits, backward-compatible with the old encoding.disconnect()leaves the WebSocket (and its keepalive) alive whenlogout()rejects — the common case, since you usually disconnect because the network is bad.caption: ""attached.raw-messageeither).Features
formatJid()always addresses<digits>@s.whatsapp.net, which WhatsApp answers with ack 463 for LID-only contacts and which forks the conversation in two. We route outbound sends throughsignalRepository.lidMapping, return the actually-addressed JID asMessage.resolvedJid, and exposegetPNForLID()/getLIDForPN()for raw-message consumers. Complements the TC-token work (which expects LIDs anyway).close()— stop a session without unlinking the device. Right now the only teardown isdisconnect()→logout(), so a rolling deploy or a graceful SIGTERM unlinks every session from its phone.socketFactory,authStateFactoryand aversionoption — this is what let us write 60+ provider tests with a hand-driven fake instead of module mocks; the fake ships with the first feature PR.We use all of this in production, and we're happy to keep maintaining what lands (Discord too, if that's easier for coordination).