Skip to content

fix(protocol): match the own account's hosted devices in isOwnAccountJid - #241

Merged
vinikjkkj merged 1 commit into
masterfrom
fix/hosted-jid-own-account
Aug 11, 2026
Merged

fix(protocol): match the own account's hosted devices in isOwnAccountJid#241
vinikjkkj merged 1 commit into
masterfrom
fix/hosted-jid-own-account

Conversation

@vinikjkkj

@vinikjkkj vinikjkkj commented Aug 11, 2026

Copy link
Copy Markdown
Owner

WhatsApp delivers hosted sessions addressed as @hosted / @hosted.lid instead of @s.whatsapp.net / @lid. isOwnAccountJid compared the preserved server, so @hosted.lid never matched a meLid of @lid: same account, different server. Every message the account authored from such a device came back with fromMe false.

That cascades. selfSentChat is gated on fromMe, so the chat stayed unresolved and remoteJid fell back to the from attr, which is the account itself, while remoteJidAlt took sender_pn, which is the account's own number. Consumers then resolved the contact to the connection's own number and collapsed traffic addressed to many different peers into a single thread, stored as received.

Both sides are now canonicalized before comparison, mirroring the web bundle: isMeAccount defers to isSameAccountAndAddressingMode, which maps hosted to c.us and hosted.lid to lid and then compares the user alone, ignoring the device. Note this only helps when meLid is populated - with an empty meLid there is nothing for a canonicalized LID to match against.

buildIncomingMessageKey also stops gating the recipient promotion on the chat resolving. When a 1:1 stanza is self-authored the sender attrs describe the own account whether or not the chat resolved, so they must never reach remoteJidAlt. The msmsg and unavailable call sites pass no destinationJid, so that path is reachable.

toUserJid keeps its zero-allocation fast path under canonicalization when the server is not hosted, since canonicalization rewrites nothing else - without it every identity comparison paid a parse and a slice. isHostedDeviceJid now shares that same in-place check instead of a second tail-anchored scan, which leaves the hosted-server detection in one place.

Review in cubic

Summary by CodeRabbit

  • Bug Fixes
    • Improved handling of direct messages sent from hosted devices.
    • Correctly identifies hosted-device messages as self-sent and preserves recipient addressing, even when the chat cannot be resolved.
    • Improved account matching across hosted and standard device address formats.
    • Added validation for hosted-device addresses to reject malformed or mismatched identities.

WhatsApp delivers hosted sessions addressed as @HosteD / @hosted.lid instead
of @s.whatsapp.net / @lid. isOwnAccountJid compared the preserved server, so
<user>@hosted.lid never matched a meLid of <user>@lid: same account, different
server. Every message the account authored from such a device came back with
fromMe false.

That cascades. selfSentChat is gated on fromMe, so the chat stayed unresolved
and remoteJid fell back to the `from` attr, which is the account itself, while
remoteJidAlt took sender_pn, which is the account's own number. Consumers then
resolved the contact to the connection's own number and collapsed traffic
addressed to many different peers into a single thread, stored as received.

Both sides are now canonicalized before comparison, mirroring the web bundle:
isMeAccount defers to isSameAccountAndAddressingMode, which maps hosted to
c.us and hosted.lid to lid and then compares the user alone, ignoring the
device. Note this only helps when meLid is populated - with an empty meLid
there is nothing for a canonicalized LID to match against.

buildIncomingMessageKey also stops gating the recipient promotion on the chat
resolving. When a 1:1 stanza is self-authored the sender attrs describe the
own account whether or not the chat resolved, so they must never reach
remoteJidAlt. The msmsg and unavailable call sites pass no destinationJid, so
that path is reachable.

toUserJid keeps its zero-allocation fast path under canonicalization when the
server is not hosted, since canonicalization rewrites nothing else - without
it every identity comparison paid a parse and a slice. isHostedDeviceJid now
shares that same in-place check instead of a second tail-anchored scan, which
leaves the hosted-server detection in one place.
@github-actions github-actions Bot added the fix Bug fix label Aug 11, 2026
@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Hosted-device JID handling now detects and canonicalizes hosted variants for account matching. Self-sent direct messages now promote recipient addressing even when chat resolution fails. Tests cover hosted-device identity matching, detection, and incoming message fields.

Changes

Hosted-device identity handling

Layer / File(s) Summary
Hosted-device JID normalization and account matching
src/protocol/jid.ts, src/protocol/__tests__/protocol.test.ts
Hosted-server detection is shared by JID parsing and hosted-device checks. Account matching canonicalizes hosted PN and LID forms. Tests cover valid and invalid hosted-device JIDs and account variants.
Self-sent direct message addressing
src/message/primitives/incoming.ts, src/message/primitives/__tests__/incoming.test.ts
Self-sent direct messages promote recipient addressing without requiring a resolved chat. Tests cover resolved and unresolved hosted-device messages.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change to hosted-device matching in isOwnAccountJid.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/hosted-jid-own-account

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.

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/protocol/jid.ts`:
- Around line 214-230: Update the hosted-device JID validation around
isHostedServerAt so the server segment contains no additional @ characters after
the separator. Perform this check before returning true from the device branch,
ensuring malformed values such as hosted@extra are rejected while valid hosted
device JIDs retain their current behavior.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: c8ae6fc8-fcad-4ee7-b7e8-9093774c4359

📥 Commits

Reviewing files that changed from the base of the PR and between 460c9b0 and bb63d17.

📒 Files selected for processing (4)
  • src/message/primitives/__tests__/incoming.test.ts
  • src/message/primitives/incoming.ts
  • src/protocol/__tests__/protocol.test.ts
  • src/protocol/jid.ts

Comment thread src/protocol/jid.ts
Comment on lines +214 to +230
/**
* Returns `true` when the server segment of `jid` starting at `from` is one of
* the hosted variants. Compares in place, so the common miss costs one length
* check and allocates nothing. Callers that already located the `@` pass its
* index rather than paying {@link isJidType}'s tail-anchored rescan.
*/
function isHostedServerAt(jid: string, from: number): boolean {
const length = jid.length - from
if (length === WA_DEFAULTS.HOSTED_SERVER.length) {
return jid.startsWith(WA_DEFAULTS.HOSTED_SERVER, from)
}
if (length === WA_DEFAULTS.HOSTED_LID_SERVER.length) {
return jid.startsWith(WA_DEFAULTS.HOSTED_LID_SERVER, from)
}
return false
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

fd -t d '^(wa-web|wa-mob)$' . | while IFS= read -r directory; do
    printf '\n== %s ==\n' "$directory"
    rg -n -C 4 \
        'isSameAccountAndAddressingMode|isMeAccount|hosted\.lid|`@hosted`|isHostedDevice' \
        "$directory" || true
done

printf '\n== Current implementation and coverage ==\n'
rg -n -C 5 \
    'isHostedServerAt|isOwnAccountJid|isHostedDeviceJid|canonicalizeSignalServer' \
    src/protocol/jid.ts src/protocol/__tests__/protocol.test.ts

Repository: vinikjkkj/zapo

Length of output: 15299


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '== repository protocol files =='
git ls-files | rg '(^|/)(wa-web|wa-mob)(/|$)|src/protocol/(jid|constants|index)\.ts$|package\.json$' || true

printf '%s\n' '== JID definitions and constants =='
rg -n -C 8 \
    'HOSTED_SERVER|HOSTED_LID_SERVER|HOST_DOMAIN|LID_SERVER|function isHostedServer|function isHostedDeviceId|function parseSignalAddressFromJid|function isOwnAccountJid|function isMeAccount' \
    src/protocol src/message

printf '%s\n' '== repository references to hosted JIDs =='
rg -n -S -C 3 '`@hosted`|hosted\.lid|isOwnAccountJid|isHostedDeviceJid|canonicalizeSignalJid' . \
    -g '!node_modules' -g '!dist' -g '!build' | head -n 500

Repository: vinikjkkj/zapo

Length of output: 50370


🌐 Web query:

WhatsApp Web isSameAccountAndAddressingMode isMeAccount hosted.lid hosted JID

💡 Result:

In the context of WhatsApp Web and third-party libraries like Baileys or WhatsMeow, the terms mentioned relate to identity, addressing, and infrastructure routing within WhatsApp's multi-device and business architecture [1][2][3]. Hosted JIDs and Infrastructure WhatsApp uses specific JID (Jabber ID) suffixes to distinguish account types and their routing infrastructure [3][4]. - hosted: Used for phone-number-based (PN) business accounts hosted by Meta [3][5]. - hosted.lid: Used for accounts that have migrated to or natively use LID (Local Identifier) addressing while being hosted by Meta [3][5]. isMeAccount and isHostedMeAccount These are internal flags or utility functions used by WhatsApp Web and libraries to determine if a specific user JID belongs to the currently authenticated session or is a business account requiring special handling [1][2][6]. - isMeAccount (often implemented via helper functions like isMe or similar checks) verifies if a given JID matches the local user's JID [1]. - setIsHostedMeAccount is specifically invoked during the pairing process for business accounts hosted on Meta infrastructure, ensuring correct cryptographic signatures (like ADV signatures) are generated [2]. isSameAccountAndAddressingMode This concept relates to the normalization of JIDs across different addressing modes (PN vs. LID) [7]. Because a single WhatsApp user may be reachable via both a PN JID (e.g., 15551234567@s.whatsapp.net) and a LID JID (e.g., 1234567890@lid), systems must determine if two different identifiers refer to the same logical account [7][3]. - Logic like areJidsSameUser is used to abstract away the underlying addressing mode, ensuring that cryptographic sessions are not duplicated or mismanaged when a user migrates between or uses mixed addressing [7][3]. - When handling hosted accounts, systems must map these correctly to their respective LID or PN equivalents to maintain session consistency, often using shared caches for LID/PN associations [8][7]. These components together ensure that WhatsApp's privacy-preserving LID system functions seamlessly alongside legacy PN addressing and modern business-hosted infrastructure [7][3].

Citations:


🌐 Web query:

site:github.com/WhiskeySockets/Baileys "isSameAccountAndAddressingMode"

💡 Result:

The term isSameAccountAndAddressingMode does not appear to be a standard or public API function in common programming libraries or the Baileys WhatsApp library context [1][2][3][4]. It is likely a hypothetical or custom-defined utility function, or a misremembered name for logic related to handling WhatsApp addressing modes (PN/LID) [3][5]. In the context of the Baileys library (often associated with these types of identifiers), logic exists to manage and compare addressing modes—specifically between PN (Phone Number) and LID (Lid/User ID) identifiers [6][3][5]. Developers frequently use utility functions like extractAddressingContext to determine whether a message is being addressed via LID or PN, and compare these states when processing incoming stanzas [3][7]. If you are encountering this in a specific codebase, it is likely a local helper function designed to: 1. Verify if two JIDs (Jabber IDs) refer to the same account [1][4]. 2. Confirm that the addressing modes (e.g., LID vs PN) match or are compatible between two entities [3][8]. For exact usage, please check your project's local utility files or the specific repository where you encountered the term, as it is not part of the standard Baileys public API [1][2][3].

Citations:


🌐 Web query:

site:github.com/tulir/whatsmeow hosted.lid hosted device 99 JID

💡 Result:

In the context of the WhatsApp infrastructure—specifically within libraries like whatsmeow—hosted.lid is a JID (Jabber ID) server domain used for "Hosted LID" (LID stands for Linked Identifier) accounts [1]. Key details regarding this JID format: * Hosted Accounts: These refer to business accounts that are hosted on WhatsApp infrastructure, often associated with Business-on-behalf-of (coexistence/coex) scenarios [2][3]. * Role of hosted.lid: While standard WhatsApp users typically use s.whatsapp.net and hidden/privacy-focused users use lid as their server domain, hosted.lid is used specifically for devices associated with hosted business accounts that utilize LID-based addressing [1]. * JID Structure: A JID associated with this domain typically follows the format [identifier]:[device_id]@hosted.lid`` [1]. The device_id (e.g., `99` in your query) represents the specific device index connected to that account [1]. * Context: This server domain is used by client libraries to correctly route traffic and manage cryptographic sessions (like Signal sessions) for hosted business entities, distinguishing them from standard user accounts [1][4][5]. It is primarily a technical distinction used in the WhatsApp protocol for managing identity and encryption in a hosted business environment [3][4].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '== focused call sites =='
rg -n -C 8 \
    'isOwnAccountJid|isHostedDeviceJid|canonicalizeSignalJid|canonicalizeOwnAccountJid' \
    src/message/primitives/incoming.ts src/client/WaClientFactory.ts src/retry/replay.ts \
    src/signal src/protocol/jid.ts

printf '%s\n' '== complete hosted-device implementation =='
sed -n '338,372p' src/protocol/jid.ts

printf '%s\n' '== complete account comparison implementation =='
sed -n '268,322p' src/protocol/jid.ts

Repository: vinikjkkj/zapo

Length of output: 29175


🏁 Script executed:

#!/bin/bash
set -euo pipefail

for url in \
  'https://raw.githubusercontent.com/tulir/whatsmeow/main/types/jid.go' \
  'https://raw.githubusercontent.com/WhiskeySockets/Baileys/master/src/WABinary/jid-utils.ts' \
  'https://raw.githubusercontent.com/WhiskeySockets/Baileys/master/src/Utils/decode-wa-message.ts'
do
    printf '\n== %s ==\n' "$url"
    curl -L --fail --silent --show-error "$url" |
        rg -n -C 8 'hosted|HOSTED|isMe|same.*account|device.*99|99.*device|addressing' || true
done

Repository: vinikjkkj/zapo

Length of output: 9674


🏁 Script executed:

#!/bin/bash
set -euo pipefail

url='https://raw.githubusercontent.com/WhiskeySockets/Baileys/master/src/WABinary/jid-utils.ts'
curl -L --fail --silent --show-error "$url" | sed -n '90,180p'

printf '\n== focused local behavior probe ==\n'
python3 - <<'PY'
HOSTED = ('hosted', 'hosted.lid')
HOSTED_ID = 99

def is_hosted_device_jid(jid):
    at = jid.find('@')
    if at < 1 or at >= len(jid) - 1:
        return False
    server = jid[at + 1:]
    if server in HOSTED:
        return True
    colon = jid.find(':')
    if colon < 0 or colon >= at - 1:
        return False
    device = jid[colon + 1:at]
    return device.isdigit() and int(device) <= 2**53 - 1 and int(device) == HOSTED_ID

for jid in (
    '5511@hosted',
    '5511@hosted.lid',
    '5511:99@lid',
    '5511:1@lid',
    'a@b@hosted',
    '5511:99@hosted@extra',
    '`@hosted`',
    '5511@hosted.li'
):
    print(f'{jid}: {is_hosted_device_jid(jid)}')
PY

Repository: vinikjkkj/zapo

Length of output: 2021


Reject JIDs with extra @ characters before checking the device ID.

isHostedDeviceJid('5511:99@hosted@extra') returns true because the device branch parses only up to the first @. This can mark malformed requesters as hosted and add sender_intent: hosted. Reject additional @ characters before returning true.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/protocol/jid.ts` around lines 214 - 230, Update the hosted-device JID
validation around isHostedServerAt so the server segment contains no additional
@ characters after the separator. Perform this check before returning true from
the device branch, ensuring malformed values such as hosted@extra are rejected
while valid hosted device JIDs retain their current behavior.

Source: Coding guidelines

@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.

No issues found across 4 files

Re-trigger cubic

@vinikjkkj
vinikjkkj merged commit 432d5fb into master Aug 11, 2026
30 checks passed
@vinikjkkj
vinikjkkj deleted the fix/hosted-jid-own-account branch August 11, 2026 18:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

fix Bug fix

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant