Skip to content

sync: master → develop 2026-06-14 — pull #546 release + auto-version bumps#549

Merged
rsalcara merged 29 commits into
developfrom
sync/master-to-develop-2026-06-14
Jun 14, 2026
Merged

sync: master → develop 2026-06-14 — pull #546 release + auto-version bumps#549
rsalcara merged 29 commits into
developfrom
sync/master-to-develop-2026-06-14

Conversation

@rsalcara

@rsalcara rsalcara commented Jun 14, 2026

Copy link
Copy Markdown
Owner

Resumo

Sync periódico master → develop após o merge da PR #546. Develop estava 27 commits atrás. Merge clean, sem conflitos.

O que vem nessa sync (27 commits)

Audit fixes da release #546 (8)

  • `7fdf1898b7` chore: merge master version bumps into release branch
  • `8b0176b8fa` fix(PR release: develop → master 2026-06-13 — VoIP + 6 audit batches + version sync #546 audit): tr range BLOCKER + 5 VoIP P1s + JID-HOSTED-P2 + 4 P2/P3 polish
  • `53cbf6399c` fix(audit r6): FU-04 varint 5-byte overflow + FU-06 port upper-bound + FU-03 reverse-delete forward cleanup
  • `6303c2dd10` release: develop → master 2026-06-13 (merge commit)

Auto-bot version bumps (16)

PRs #516, #517, #524, #525, #527, #528, #530, #531, #539, #540, #541, #542, #543, #544, #547, #548 — atualizações automáticas de `baileys-version.json` e `WAProto/*`.

Release commits anteriores (3)

Estado pós-sync

Item Antes Depois
`baileys-version.json` `[2,3000,1041103517]` `[2,3000,1041437765]` ✅
`update-proto.yml` `tr` bug presente corrigido ✅
Audit fixes round 5/6 ausentes presentes ✅
Diff develop ↔ master 27 atrás 0 (igualados)

Customizações intactas

Carrossel, lista, botão, poll, view-once, biz quality_control, Lottie wrap, Meta AI msmsg, DSM, TC token, LID↔PN, Phase 9 multi-DB, useLegacyLock, schema migrations — todos preservados.

🤖 Generated with Claude Code


Summary by cubic

Sync master → develop after the release to align versions and bring audit fixes that improve VoIP reliability, JID @hosted.lid handling, Signal varint parsing, env/metrics robustness, and the proto auto-update workflow.

  • Bug Fixes

    • CI: fix tr character class in .github/workflows/update-proto.yml to stop Summary step failures.
    • VoIP: bail when any destination encryption fails; detach CB:call/CB:receipt listeners on disconnect; correctly handle @hosted.lid; re-register global callbacks on init, surface init errors, and add a 30s worker WASM load timeout.
    • Signal: reject 5-byte varints that overflow 32-bit in libsignal.ts.
    • Env/Metrics: intFromEnv/floatFromEnv trim input; intFromEnv adds max and clamps Prometheus port to 1–65535.
    • Multi-DB SQLite: reverse-delete also clears legacy forward entries; cache statement for getAllLidsForPn; clarify error handling comments.
  • Dependencies

    • Update WAProto/* and src/Defaults/baileys-version.json to the latest WhatsApp Web/proto versions (WAProto.proto header and baileys-version.json now at 2.3000.1041437765).

Written for commit 91df359. Summary will update on new commits.

Review in cubic

rsalcara and others added 29 commits June 6, 2026 23:41
…f path (#513 review)

PR #513 chatgpt-codex review (P2) caught a real gap that PR #509 introduced
when it added the `maxKeys` cap to `userDevicesCache`:

  - PR #509 added `safeCacheSet` handling to the cache-population path in
    `messages-send.ts:528-548` (where USync fills the cache).
  - The OTHER call-site that writes to `userDevicesCache` —
    `messages-recv.ts:2240` in the device-notification handler ('add' /
    'remove' tags) — was left unprotected.

`@cacheable/node-cache`'s capacity check is `keyCount() + 1 > maxKeys` —
applied BEFORE the engine checks whether the key already exists. So once
the cache reaches its `maxKeys` ceiling (5,000 entries), even an UPDATE
to an already-cached user throws ECACHEFULL. The guard
`if (!existingCache.length) continue` at line 2210 doesn't help: it only
short-circuits when the user is NOT cached. A device-list update for an
already-cached user would still hit the `.set` and throw.

Real impact under sustained gateway load:

  - Cache hits 5,000 entries (a few dozen active groups will get there).
  - A device-add/remove notification arrives for one of the cached users.
  - `userDevicesCache.set(...)` throws ECACHEFULL.
  - The throw propagates into the message-receive handler and lands in
    Baileys' outer error boundary — connection survives, but a log-level
    error is emitted and the affected user's device list is NOT updated
    in the cache.
  - Next message-send for that user fetches the fresh list via USync
    (`getUSyncDevices`), which IS guarded — so the durable behavior
    eventually recovers.

The fix routes the device-notif write through the same `safeCacheSet`
helper PR #509 used in messages-send.ts. Same swallow-ECACHEFULL semantics,
same debug log, same `getUSyncDevices` fallback path. Symmetric handling
across the two call-sites that update the cache.

Test plan:
  - npm run build ✓ (3 phases pass, zero errors)
  - 1 line of production change in `messages-recv.ts:2240`, plus comment
    explaining the WHY. No test changes needed — `safeCacheSet`'s
    behavior is already covered by `cache-utils.test.ts`.

Out of scope (intentionally NOT touched):
  - Carousel, lists, buttons, polls, view-once, biz quality_control,
    useLegacyLock, TC token custom flow, LID↔PN batched, Phase 9 multi-DB,
    lidDbMigrated:false, the cacheMetricsInterval memory-leak fix, schema
    migrations + statement cache + busy retry.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…#513)

release: develop → master 2026-06-06 (v2) — #509 + #510 + #511 + #512 (#513)
Co-authored-by: rsalcara <rsalcara@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Merges 6 PRs landed on `develop` since the previous master sync
(2026-06-06), preserves the two auto-bumps that have since landed on
master (#516, #517), and ships them together.

PRs included in this release
============================

  * #514 — chore(build): exclude .md from src→lib mirror
    Internal architecture notes (`src/Utils/multi-db-sqlite/ARCHITECTURE.md`,
    `USAGE.md`) no longer ship in the published package. Disk savings on
    consumer side + stops leaking internal phase planning into anyone
    that runs `npm install`.

  * #515 — chore(logs): compact recoverable Signal Protocol failures
    Recoverable Signal failures (Bad MAC, MessageCounterError, old
    counter, Invalid PreKey ID) are now compact one-liners at `warn`
    instead of three multi-KB `error` entries each. Per-event log
    output: ~10KB → ~250B. Unknown errors keep full stack + stanza.
    Net: error-dashboard noise drops dramatically without losing
    real signal.

  * #518 — feat(meta-ai): decrypt msmsg via empirically-validated WA Web
    Meta AI / FBID-bot replies (`<enc type="msmsg">`) now decrypt
    instead of NACKing with MissingMessageSecret. Algorithm
    reverse-engineered from `WAWebBotMessageSecret` via CDP and
    validated LIVE with a Meta AI "oi" capture. Five distinct bugs in
    upstream PR WhiskeySockets#2592 fixed (unbounded cache, cross-tenant leak, no
    FBID dispatch, drops streaming chunks, raw-id cache key, 12-strat
    brute force). Per-socket bounded LRU + `flushAll() + close()` on
    socket end to release the NodeCache timer.

  * #519 — feat(stickers): support lottieStickerMessage for animated .was
    Wraps + unwraps Lottie stickers (`application/was`) in
    `lottieStickerMessage` (FutureProofMessage at proto field 74),
    matching `WAWebE2EProtoGenerator`'s actual implementation. Mobile
    clients silently drop Lottie stickers in the unwrapped form; this
    makes them render on Android/iOS. Send-path `getMediaType` +
    DSM `messageContextInfo` reads both walk the wrap correctly.

  * #520 — fix(decode): preserve outer messageContextInfo when
    unwrapping deviceSentMessage
    Closes the codex P1 gap left deferred from #518. When WhatsApp
    delivers a fromMe-via-linked-device message, the OUTER `Message`
    carries `messageContextInfo.messageSecret` and the inner does not
    — the previous `msg = msg.deviceSentMessage?.message || msg`
    dropped that secret entirely. New `unwrapDeviceSentMessage` does a
    per-field merge matching WA Web's `l(e)` source exactly, including
    a length-check guard on `threadId` (protobuf decodes empty
    `repeated` as `[]` not `undefined` — naive `??` would lose the
    outer thread context).

  * #513 follow-up (already on master as `373a6f3622` from previous
    release) — the develop-side commit `50f9a77ef8` is the same
    change. Merge collapses both onto the master line; no behaviour
    change.

Preserved from master (NOT touched by this merge)
==================================================

  * #516 — chore: update proto/version to v2.3000.1040989513
    Auto-bumped `WAProto/WAProto.proto` + `baileys-version.json`.
    develop never touched these files, so the bump survives intact.
  * #517 — chore: update WhatsApp Web version to v2.3000.1040994085
    Auto-bumped `baileys-version.json` again. Same — develop didn't
    touch it, master's value (`{"version":[2,3000,1040994085]}`) is
    preserved verbatim post-merge.

Validation
==========

  * Auto-merge succeeded with ZERO conflicts — `messages-recv.ts` was
    the only file modified on both sides, but the edits were
    non-overlapping and git resolved them automatically.
  * `npm run build` clean on the merged tree.
  * 57/57 tests pass across the new-PR suites
    (dsm-context-info + meta-ai-msmsg + lottie-sticker-message +
    error-log-utils + process-message.protocol-guard).
  * `baileys-version.json` post-merge: `{"version":[2,3000,1040994085]}`
    — confirms master's auto-bump was preserved (NOT downgraded to
    develop's stale `1040973525`).

Customizations preserved (NOT touched)
======================================

Carrossel, lists, buttons, polls, view-once, biz `quality_control`,
`useLegacyLock`, TC token custom flow, LID↔PN batched, Phase 9
multi-DB, `lidDbMigrated:false`, `cacheMetricsInterval` memory-leak
fix, schema migrations + statement cache + busy retry.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
5 findings from the release PR #521 audit applied before promoting
develop → master. The release branch itself doesn't change; this PR
lands the fixes on develop and the release branch will be refreshed
to include them.

P2 — Thread 1 (chatgpt) — reporting-token check reads outer wrap
================================================================

`messages-send.ts:1884` gated reporting-token attachment on
`reportingMessage?.messageContextInfo?.messageSecret`. For Lottie
stickers post-PR #519 that secret lives INSIDE the
`lottieStickerMessage` wrap — the top-level read returns undefined
and the token is silently skipped. Regression vs plain sticker
behaviour.

Fix: lift `messageContextInfo` from both locations the same way the
initial-fanout DSM read does (the lift PR #519 already shipped on
line ~1198):

    const reportingMessageSecret =
      reportingMessage?.lottieStickerMessage?.message
        ?.messageContextInfo?.messageSecret
      ?? reportingMessage?.messageContextInfo?.messageSecret

Required adding `reportingMessage &&` to the surrounding `if` so
TypeScript doesn't lose the narrowing it previously got from the
inline property chain (the narrowing was the side-effect of reading
`reportingMessage.messageContextInfo` in the condition itself).

P2 — Thread 5 (coderabbit Major) — retry-resend DSM drops messageContextInfo
============================================================================

`messages-send.ts:1580` rebuilds the `deviceSentMessage` envelope on
the retry-resend path WITHOUT carrying `messageContextInfo` along.
The initial-fanout path got the lift in PR #519 (line ~1198); the
retry path was missed.

Concrete impact: when a companion device receives the RETRY of a
Lottie sticker via DSM, our own `unwrapDeviceSentMessage` finds:
  - inner = messageToSend = { lottieStickerMessage: { message: { stickerMessage, messageContextInfo } } }
  - inner.messageContextInfo = undefined (it's nested in the wrap)
  - outer.messageContextInfo = undefined (the retry envelope omitted it)
  → messageSecret = undefined, reporting token / encrypted-edit
    decryption material lost on the companion's copy.

Fix: same lift inline at the retry envelope build site.

P2 — Thread 2 (chatgpt) — OrphanMsmsgError stub goes to Signal retry path
=========================================================================

When `decryptMsmsgBotMessage` raises `OrphanMsmsgError` (cache miss),
the catch in `decode-wa-message.ts` sets
`messageStubType = CIPHERTEXT` and
`messageStubParameters[0] = String(err.message)`. The receive handler
at `messages-recv.ts:3115` checks for known stub-param strings and
falls through to the Signal retry / PDO placeholder-resend path when
none match — burning retry budget asking the bot for prekeys it has
no business issuing, for a problem (missing CACHE entry) that a
Signal retry can never fix.

Fix: add a guard for `messageStubParameters[0]?.startsWith('decryptMsmsgBotMessage:')`
right after the `MISSING_KEYS_ERROR_TEXT` branch. Plain ACK (no NACK,
no retry) so the server considers the message delivered. The next
bot reply that arrives after the outgoing-secret cache populates
will decrypt cleanly.

NOT a NACK MissingMessageSecret: that would tell the server to
retransmit, and the retransmission will hit the same orphan state
until the outgoing-side cache is populated (deferred per PR #518).

P3 — Thread 4 (copilot) — `__internal` export not consumed
==========================================================

`src/Utils/meta-ai-msmsg.ts` exported an `__internal` bag of helpers
(`BOT_MESSAGE_INFO`, `KEY_LENGTH`, `isMeJid`, `deriveKeyAndDecrypt`,
`decodeDecryptedMsmsg`, `userOnlyJid`, `isJidGroup`) that no file in
the repo actually imports. Grep confirms zero call sites. Removed —
the helpers stay module-private as intended.

P3 — Thread 3 (copilot) — `(err as any)?.message` in auth-utils
===============================================================

`src/Utils/auth-utils.ts:714` (the trace I added in PR #515 to log
`transactWith rolled back` without duplicating the stack) used a
plain `(err as any)?.message` cast. Tightened to
`err instanceof Error ? err.message : String(err)` — no runtime
change, just removes the `any` cast and gives string fallback for
non-Error throws.

Validation
==========

  * `npm run build` clean (TS narrowing fixed via explicit
    `reportingMessage &&` guard).
  * 57/57 tests pass across the new-PR suites
    (dsm-context-info-preservation + meta-ai-msmsg +
    lottie-sticker-message + error-log-utils +
    process-message.protocol-guard).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Two findings from the cubic re-review on PR #521 that the previous
audit-fix commit (cherry-picked from closed PR #522) didn't cover.

Issue D (P2) — test re-implemented production helper
====================================================

cubic thread 8: `dsm-context-info-preservation.test.ts` kept a local
copy of `unwrapDeviceSentMessage` and asserted against it. Comment
claimed "if rules change in decode-wa-message.ts these break first"
but that was wrong — the assertions tested the LOCAL copy, not
production. Reverting `decode-wa-message.ts` to the old `msg =
msg.deviceSentMessage?.message || msg` would leave all 10 tests
green. The tests were validating themselves.

Fix:
  * `decode-wa-message.ts` — flipped the helper from module-private
    `const` to `export const unwrapDeviceSentMessage`. The function is
    a leaf utility; exporting it surfaces nothing operational beyond
    what tests need.
  * `dsm-context-info-preservation.test.ts` — deleted the re-derived
    copy, imported from `../../Utils/decode-wa-message`. Header
    comment updated to explain WHY we now import (production parity)
    instead of re-derive.

Issue E (P3) — `if (!err)` too broad in compactError
====================================================

cubic thread 9: `error-log-utils.ts:33` short-circuited every falsy
input (including `0`, `''`, `false`, `NaN`) to the literal string
`'Unknown'`. Those are unusual but valid thrown values — code that
does `throw 0` would get its actual value erased. Operational impact
near zero (Signal Protocol throws `Error` instances), but the
contract is wrong.

Fix:
  * `error-log-utils.ts` — `if (!err)` → `if (err == null)`. Comment
    added explaining the nullish-only narrowing.
  * `error-log-utils.test.ts` — new test `preserves falsy primitives
    that are NOT null/undefined` pins the new contract: `compactError(0)`
    → `'0'`, `compactError(false)` → `'false'`, etc.

Validation
==========

  * `npm run build` clean
  * 58/58 tests pass (5 suites: dsm-context-info + error-log-utils +
    meta-ai-msmsg + lottie-sticker-message + process-message).
    Was 57; +1 from the new falsy-primitives test.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
CI lint failed on the previous commit:
  - error-log-utils.ts:36 — `if (err == null)` violated the codebase's
    `eqeqeq` rule even though `== null` is the idiomatic nullish check.
    Expanded to `if (err === null || err === undefined)` for the same
    semantics with strict-equality compliance.
  - meta-ai-msmsg.ts:46 — `isJidGroup` was imported but its only
    consumer (the now-removed `__internal` export bag from cubic
    audit thread 4) is gone. Removed from the import list.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
cubic re-review (thread 13, confidence 9) caught that the previous
ACK guard was too broad:

  if (msg?.messageStubParameters?.[0]?.startsWith('decryptMsmsgBotMessage:'))

`decryptMsmsgBotMessage` throws with a `decryptMsmsgBotMessage:` prefix
for several distinct conditions, not just `OrphanMsmsgError`:

  - `'decryptMsmsgBotMessage: no messageSecret for ${cacheKey}'`
    ← OrphanMsmsgError — cache miss, ACK + wait for cache to populate
  - `'decryptMsmsgBotMessage: missing meta.target_id'`
    ← malformed stanza — deserves NACK/retry
  - `'decryptMsmsgBotMessage: MessageSecretMessage missing encIv/encPayload'`
    ← malformed proto — deserves NACK/retry
  - real AES-GCM auth-tag mismatch (string varies but may also start
    with `decryptMsmsgBotMessage:` if wrapped)
    ← deserves the Signal retry path

The broader prefix silently ACK'd all of those, hiding real protocol
failures from the server-side retry machinery. Narrow the match to
the exact substring that uniquely identifies the orphan-cache case:

  startsWith('decryptMsmsgBotMessage: no messageSecret for ')

Other `decryptMsmsgBotMessage:` failures now flow normally to the
Signal retry / PDO placeholder-resend path (where they belong).

Comment expanded to document why the narrow match is required.

Validation:
  * build clean
  * 58/58 tests still pass

Thread 12 (P3) — `String(err)` in auth-utils.ts:714 — is cosmetic
and deferred per the audit recommendation.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
release: develop → master 2026-06-08 (5 PRs)
Co-authored-by: rsalcara <rsalcara@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: rsalcara <rsalcara@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: rsalcara <rsalcara@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: rsalcara <rsalcara@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: rsalcara <rsalcara@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: rsalcara <rsalcara@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Integrates the WhatsApp Web / proto auto-update PRs #527 #528 #530 #531
#539 #540 #541 #542 #543 #544 from master so the release branch carries
the most recent `baileys-version.json` / `WAProto/*` while still bringing
the feature/fix work from develop.

# Conflicts:
#	WAProto/WAProto.proto
#	src/Defaults/baileys-version.json
#	src/Socket/messages-recv.ts
#	src/Socket/messages-send.ts
… P2/P3 polish

Round-6 audit on the release branch flagged 1 blocker and 9 real findings.
All applied directly on this branch so the PR merges into master clean.

## BLOCKER — `update-proto.yml` `tr` character class

`tr -cd 'A-Za-z0-9:/?&=._-+~%#'` parses `_-+` as a range from `_` (0x5F)
to `+` (0x2B), which GNU `tr` rejects with "reverse collating sequence
order" and exits 1. The Summary step would crash on every auto-update
run, breaking the entire `update-proto` pipeline that lands in master.
Move the `-` to the end of the class so it's interpreted literally.

## VoIP P1s (5)

bridge.ts — `#sendBatchEncryptedCall` no longer pushes the stanza when
ANY destination failed to encrypt. Earlier the loop stripped `enc` from
every destination on a single failure but then fell through to
`sendCallStanza`, delivering a key-less offer that the peer couldn't
decrypt — call setup failed silently.

wasm-engine/instance.ts — three independent issues:
  (a) `#registerGlobalCallbacks` is no longer gated on a static flag.
      The map of listeners held closures over `this.#config.callbacks`,
      so a reconnect-after-disconnect routed events to handlers from
      the destroyed instance. `destroy()` now clears the static map
      and the init path always re-registers against the live instance.
  (b) `initVoipStack`'s outer catch no longer hides errors. The stack
      now records the failure in `#voipStackInitError`, and
      `waitForVoipStackReady()` re-throws it on the consumer side
      instead of silently resolving as "ready".
  (c) `#loadWasmModuleToWorker` arms a 30 s timeout that rejects the
      load promise. Without it, a worker that crashed before sending
      its `loaded` message left the engine init hanging forever.

index.ts — `disconnect()` now detaches the `'CB:call'` and
`'CB:receipt'` listeners it installed on `this.#sock.ws` before nulling
the engine refs. Earlier the listeners outlived the engine, and an
in-flight stanza would call into a torn-down `this.#engine`, throwing
into the host process.

## JID-HOSTED-P2 (5 locations)

bridge.ts (4) + index.ts (1) used `endsWith('@lid')` to gate LID
handling, which silently rewrote `*.@hosted.lid` accounts (device 99)
as PNs. Switched to `decoded.server` lookup in `bridge.ts` (the JID is
already decoded right above each check) and an explicit
`'@hosted.lid'` branch in `index.ts`.

## P2/P3 polish (4)

lid-mapping-backend.ts (MDB-UNCACHED-P2) — `getAllLidsForPn` now uses
a cached `selectAllLidsByPn` statement instead of compiling SQL per
call. Same hot-path optimisation that was already in place for
`deleteMapping`.

env-utils.ts (ENV-TRIM-P2) — `intFromEnv` and `floatFromEnv` now
`.trim()` before the empty-string check. A `KEY= ` env var with only
whitespace used to slip past `=== ''` and `Number('   ')` returned 0,
masquerading as a legitimate zero config.

keys-with-jid-map.ts (COMMENT-BESTEFF-P3) — comment said the try/catch
was "best-effort" but the catch arm always rethrows. Rewritten to
match what the code actually does: the wrapper exists only to classify
SQLITE_BUSY vs other errors.

prometheus-metrics.ts (COMMENT-PORT-P3) — comment claimed `≥1024
unprivileged` but `intFromEnv(..., 1)` allows privileged ports too.
Rewritten to acknowledge `min=1` is just "not zero / not negative" so
operators running as root can bind to a privileged port if desired.

## Validation

- `tsc --noEmit -p tsconfig.json` exit 0
- `eslint` on all 7 touched files: 0 errors after prettier autofix
- Local jest: 2 pre-existing failures in `multi-db-backends.test.ts` and
  `multi-db-sqlite-auth-state.test.ts` are missing `better-sqlite3`
  native binding on Windows toolchain — CI Linux is the gate, those
  pass there.

## Customizations untouched

Carousel, list, button, poll, view-once, biz quality_control, Lottie
wrap, Meta AI msmsg, DSM, TC token, LID↔PN batched, Phase 9 multi-DB,
useLegacyLock, schema migrations, memory leak fix — all unchanged.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: rsalcara <rsalcara@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
…+ FU-03 reverse-delete forward cleanup

Round-6 audit of PR #546 surfaced 9 follow-up items. After case-by-case
validation, 3 are real and acionable, 6 are intentional non-fixes —
documented below.

## Applied (3)

FU-04 — `libsignal.ts` varint 5-byte overflow
  The earlier round-5 fix rejected 6-byte varints via `shift >= 35`.
  This closes the remaining loophole: a 5-byte varint whose 5th byte has
  the top nibble set (`byte & 0xf0`) encodes a value > 2³² – 1 that
  `>>> 0` silently masks into a valid-looking uint32. Reject it
  explicitly at `shift === 28` before the bitwise-or runs.

FU-06 — `prometheus-metrics.ts` port upper-bound
  `intFromEnv` gained an optional `max` parameter (default
  `Number.MAX_SAFE_INTEGER`, so existing call sites are unaffected).
  Prometheus port now passes `max=65535` so a `BAILEYS_PROMETHEUS_PORT=99999`
  falls back to the default `9092` instead of reaching `server.listen()`
  with an out-of-range integer.

FU-03 — `keys-with-jid-map.ts` reverse-delete forward cleanup
  Reverse delete (`${lid}_reverse → null`) wiped the typed `jid_map` row
  and the inner store's `_reverse` entry but left any legacy forward
  entry `pnUser → lidUser` in the inner store. A subsequent
  `inner.get('lid-mapping', [pnUser])` would then resurrect the
  just-deleted LID via the fallback path. We now resolve the PN
  synchronously via `jidMap.getPnForLid(lidUser)` and queue its
  forward delete on the same pass.

## NOT applied (with reasons)

FU-01 — pinning `actions/setup-node@v4` etc. to immutable SHAs
  The project's policy treats first-party GitHub Actions (`actions/*`)
  as trusted via major tag and pins only third-party actions by SHA
  (see `meeDamian/github-release@7ae19492...` in publish-release.yml).
  Changing only `setup-node` would be inconsistent. If the policy
  changes, this should be applied to all `actions/*` references in
  one pass — out of scope for this release.

FU-02 / FU-05 — `audio-feeder.ts` emission loop + `disconnect()` ffmpeg
  Both touch the VoIP audio-capture lifecycle. The current behaviour
  (loop stops with ffmpeg, child process left to its natural exit) is
  intentional for the silence-source path and the finite-source path
  has a documented limitation. A proper fix needs E2E coverage of the
  capture state machine first — left as a tracked follow-up.

FU-07 — `worker-modules.js` flatMap polyfill + WebCodecs check
  `worker-modules.js` is vendored verbatim from WhatsApp Web's bundle
  (Meta source). Patching it would create permanent divergence and
  break our ability to refresh the bundle on schema bumps. The
  `worker-bootstrap.ts` shim already polyfills `window`, so the
  WebCodecs throw doesn't fire in practice.

FU-08 — `relay-transport.ts:327` early-packet migration
  Migrating buffered packets from a placeholder connection to the
  canonical connection after `updateRelayList()` is a multi-week
  redesign of the relay handshake. Edge case in early-init; deferred.

FU-09 — `worker-bootstrap.ts:445` `importScripts` CWD fallback
  Only fires when the vendored loader.js calls `importScripts` with a
  relative URL — never observed in practice in the WASM bundle we
  ship.

MAC stream verification at `final()` only — design trade-off
  Documented in `messages-media.ts`. Buffering the entire decrypted
  payload until the MAC verifies would defeat the purpose of streaming
  downloads. The PR moved the project from "no MAC check at all" to
  "MAC check at final()", which is a strict improvement. A
  "verify-before-emit" mode could be added later as opt-in for
  callers who can afford the buffering.

## Validation

- `tsc --noEmit -p tsconfig.json` exit 0
- `eslint` on touched files: 0 errors
- `intFromEnv` change is backward-compatible (new `max` defaults to
  `MAX_SAFE_INTEGER`); all existing call sites (auth, retry, batching)
  retain their behaviour.

## Customizations untouched

Carousel, list, button, poll, view-once, biz quality_control, Lottie
wrap, Meta AI msmsg, DSM, TC token, LID↔PN batched, Phase 9 multi-DB,
useLegacyLock, schema migrations, memory leak fix — unchanged.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…on sync

release: develop → master 2026-06-13 — VoIP + 6 audit batches + version sync
…bumps

Brings develop up to date with master after PR #546 (release) was merged.
Master accumulated 27 commits while develop was frozen for the release
cycle:

- 11 auto-update PRs (#516, #517, #524, #525, #527, #528, #530, #531,
  #539, #540, #541, #542, #543, #544, #547, #548) — auto-bot version
  bumps for WhatsApp Web / proto
- 6 release commits (release/* prefixes from past develop→master syncs)
- 8 fix commits from the PR #546 release branch:
  - `7fdf1898b7` chore: merge master version bumps into release branch
  - `8b0176b8fa` fix(PR #546 audit): tr range BLOCKER + 5 VoIP P1s + JID-HOSTED-P2 + 4 P2/P3 polish
  - `53cbf6399c` fix(audit r6): FU-04 varint 5-byte overflow + FU-06 port upper-bound + FU-03 reverse-delete forward cleanup
  - `6303c2dd10` release: develop → master 2026-06-13 (the merge commit itself)

After this sync, develop carries the same `baileys-version.json`
(`[2,3000,1041437765]`) and `WAProto/*` as master, plus all the audit
fixes that landed via the release branch.
Copilot AI review requested due to automatic review settings June 14, 2026 15:00
@coderabbitai

coderabbitai Bot commented Jun 14, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6566cbae-95e6-49dd-94c9-b2e940c3408f

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch sync/master-to-develop-2026-06-14

Warning

Billing warning: we have not been able to collect payment for this subscription for more than 72 hours. Please update the payment method or pay any pending invoices in Billing to avoid service interruption.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

Copy link
Copy Markdown

Thanks for opening this pull request and contributing to the project!

The next step is for the maintainers to review your changes. If everything looks good, it will be approved and merged into the main branch.

In the meantime, anyone in the community is encouraged to test this pull request and provide feedback.

✅ How to confirm it works

If you’ve tested this PR, please comment below with:

Tested and working ✅

This helps us speed up the review and merge process.

📦 To test this PR locally:

# NPM
npm install @whiskeysockets/baileys@rsalcara/InfiniteAPI#sync/master-to-develop-2026-06-14

# Yarn (v2+)
yarn add @whiskeysockets/baileys@rsalcara/InfiniteAPI#sync/master-to-develop-2026-06-14

# PNPM
pnpm add @whiskeysockets/baileys@rsalcara/InfiniteAPI#sync/master-to-develop-2026-06-14

If you encounter any issues or have feedback, feel free to comment as well.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 11 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="src/Voip/signaling/bridge.ts">

<violation number="1" location="src/Voip/signaling/bridge.ts:297">
P1: Early return on multi-destination encryption failure drops signaling without notifying WASM. This can leave call setup hanging instead of surfacing an immediate failure.</violation>
</file>

<file name="src/Voip/wasm-engine/instance.ts">

<violation number="1" location="src/Voip/wasm-engine/instance.ts:486">
P1: Destroying one engine now unregisters global callbacks for all engines. In multi-client processes this breaks active calls on other `VoipClient` instances.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

// would deliver a key-less offer that the peer can't decrypt,
// failing the call setup silently. Bail instead so the upstream
// caller can surface the failure.
if (encryptionFailed) return

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Early return on multi-destination encryption failure drops signaling without notifying WASM. This can leave call setup hanging instead of surfacing an immediate failure.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/Voip/signaling/bridge.ts, line 297:

<comment>Early return on multi-destination encryption failure drops signaling without notifying WASM. This can leave call setup hanging instead of surfacing an immediate failure.</comment>

<file context>
@@ -283,10 +284,18 @@ export class SignalingBridge {
+			// would deliver a key-less offer that the peer can't decrypt,
+			// failing the call setup silently. Bail instead so the upstream
+			// caller can surface the failure.
+			if (encryptionFailed) return
+
 			if (includeDeviceIdentity) this.#appendDeviceIdentity(voipNode)
</file context>

// re-init can register its own listeners against the live instance.
// Same scope as the listeners themselves (process-wide singleton);
// no other live engine to step on in practice.
WasmEngine.#globalCallbackListeners.clear()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Destroying one engine now unregisters global callbacks for all engines. In multi-client processes this breaks active calls on other VoipClient instances.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/Voip/wasm-engine/instance.ts, line 486:

<comment>Destroying one engine now unregisters global callbacks for all engines. In multi-client processes this breaks active calls on other `VoipClient` instances.</comment>

<file context>
@@ -472,12 +479,22 @@ export class WasmEngine {
+		// re-init can register its own listeners against the live instance.
+		// Same scope as the listeners themselves (process-wide singleton);
+		// no other live engine to step on in practice.
+		WasmEngine.#globalCallbackListeners.clear()
+		WasmEngine.#globalCallbacksRegistered = false
 	}
</file context>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 91df3598d6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

// the destroyed instance, so events from the fresh engine were
// routed to dead handlers. `destroy()` now clears the map; init
// repopulates it for the live instance.
this.#registerGlobalCallbacks()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid duplicating WASM callbacks after init retries

When initialize() fails after this registration point (for example a worker load timeout or loader rejection), #initialized remains false and callers can retry initialization on the same WasmEngine without calling destroy(). Because the listener map is static and this now always appends fresh closures, the retry leaves multiple onSignalingXmpp/relay/audio handlers active, so later callbacks can send duplicate call stanzas or relay packets once the engine finally starts.

Useful? React with 👍 / 👎.

// from the typed backend (synchronous) and queue its
// forward delete too.
const resolvedPn = jidMap.getPnForLid(lidUser)
if (resolvedPn) innerDeleteForward.push(resolvedPn)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Don't delete unrelated legacy PN mappings on reverse deletes

For a reverse-only delete of an old LID, this queues a null write for the PN key resolved from jid_map without checking what the legacy inner forward entry currently contains. If that PN has since rotated to a newer LID that only exists in the legacy fallback store, deleting the whole PN key drops the still-valid pn → newLid mapping; the forward delete should only be propagated when the inner forward value matches the LID being removed.

Useful? React with 👍 / 👎.

@rsalcara rsalcara merged commit a36726c into develop Jun 14, 2026
6 of 7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants