- Remove 'passkey.required' and 'passkey.confirmation.required' events.
Passkey pairing is a session state, so it rides on 'session.status'
instead - one new status value per pairing step WhatsApp adds, not
one new event.
- Add PASSKEY_CONFIRMATION_REQUIRED session status.
- Add 'data' to the session.status payload - the extra info that belongs
to the current status. PASSKEY_REQUIRED carries the WebAuthn challenge,
PASSKEY_CONFIRMATION_REQUIRED carries { code }, null otherwise.
- Base session gets setStatus(status, data); the plain 'status = value'
setter delegates to it with no data, so the data clears itself as soon
as the session moves on (WORKING, STOPPED, ...) with no extra bookkeeping.
- REST: GET /auth/passkey/challenge (was GET /auth/passkey) returns the
challenge object, GET /auth/passkey/confirmation returns { code }.
Both throw 422 when nothing is pending.
- MCP: auth-passkey-challenge, auth-passkey-submit, auth-passkey-confirmation,
auth-passkey-confirm.
- Drop the SkipHandoffUX auto-confirm branch - it was dead code. whatsmeow
confirms on its own in that case and only emits passkey-confirmation for
the manual one (qrchan.go).
- Keep QR rotation from bouncing PASSKEY_CONFIRMATION_REQUIRED back to
SCAN_QR_CODE, same as PASSKEY_REQUIRED.
- New engine step: gows emits passkey-request/passkey-confirmation, session
status PASSKEY_REQUIRED, challenge stored and exposed via getPasskeyChallenge().
- REST: GET/POST /api/:session/auth/passkey, GET /api/:session/auth/passkey/confirmation,
POST /api/:session/auth/passkey/confirm.
- Webhooks: passkey.required (challenge) and passkey.confirmation.required (manual
code case; most pairings auto-confirm server-side right after the assertion).
- QR rotation no longer bounces PASSKEY_REQUIRED back to SCAN_QR_CODE.
Copy the storage (Mongo/Postgres/SQLite), media (S3/Postgres), engine
auth/store and util implementations from src/plus into src/core, and fold
the *Plus engine sessions, session manager, factories, health and channels
services into their *Core classes.
Core now supports multi-session orchestration, Mongo/Postgres session
storage, S3/Postgres media storage, FFmpeg media conversion, real health
checks and channels metadata. mediaConverter is set once in the base
session, and AppModuleCore wires the conditional media modules and health
indicators.
Session stuck in 'starting' or 'stoped' state due to Node.js Buffer limit reached when loading large RemoteAuth database entries (PostgreSQL/WebJS) #2090
Fix#2084 - All outbound messages fail with server returned error 400 until session restart
Fix#2085 - MediaRetry to also trigger on ciphertext hash mismatch (not only 403)
Fix#2080 - status@broadcast batch timeouts, add WAHA_GOWS_STATUS_PARTICIPANTS_BATCH_SIZE
Problem:
- stickerMessage often has URL https://a.whatsapp.net with no path for
encrypted media. The previous fix only copied uppercase URL to lowercase url,
so DownloadMedia still tried HTTP GET on that host (for example DNS lookup
failures) instead of using directPath and media keys.
- Lottie (application/was) stickers usually ship a full mmg.whatsapp.net URL,
so they worked; image/webp stickers with the placeholder broke.
Solution:
- Treat a.whatsapp.net with an empty path as a placeholder: clone the message
and delete both URL and url on stickerMessage before gRPC DownloadMedia.
- For real CDN URLs, keep mirroring URL to url when url is missing.
Lottie stickers arrive from WhatsApp as a ZIP archive (mimetype
application/was) containing animation/animation.json. This change
intercepts those stickers in the GOWS Plus session, renders each
animation frame off-screen, and delivers an animated WebP to the
webhook instead of the raw ZIP.
Implementation:
- src/plus/utils/lottie-converter.ts (new):
- Extracts animation.json from the ZIP via adm-zip
- Renders frames with @lottiefiles/dotlottie-web + @napi-rs/canvas
(HTMLCanvasElement polyfill required; ImageData must NOT be polyfilled
to avoid per-frame pixel caching in WASM memory)
- Drives frames manually via dotLottie.setFrame(i) after 'load' for
reliable synchronous capture
- Encodes each RGBA frame to single-frame WebP with sharp (quality=80)
- Assembles animated WebP via node-webpmux with full alpha transparency
- Frame delay derived from animation fps; clamped to >=30ms
- Fallback delay configurable via WAHA_LOTTIE_DEFAULT_DELAY_MS env var
- sharp/adm-zip/node-webpmux loaded via require() — their module.exports
is the callable itself with no .default, so ESM default import resolves
to undefined at runtime
- src/plus/engines/gows/session.gows.plus.ts:
- Overrides downloadMedia with LottieAwareGOWSEngineMediaProcessor
- normalizeStickerUrl: fixes GOWS URL field case mismatch (URL vs url)
that caused an infinite network loop on Lottie downloads
- Reports mimetype image/webp and extension .webp for Lottie stickers
- package.json: add @lottiefiles/dotlottie-web, @napi-rs/canvas,
node-webpmux to dependencies