Skip to content

Expose business identity: hosted pairing, AEA, verified-name issuer and hosted Signal addresses - #55

Open
purpshell wants to merge 10 commits into
codex/upstream-sync-20261027from
rajeh/wa-business-emulation-hypermeow
Open

purpshell wants to merge 10 commits into
codex/upstream-sync-20261027from
rajeh/wa-business-emulation-hypermeow

Conversation

@purpshell

@purpshell purpshell commented Oct 28, 2026 •

Copy link
Copy Markdown
Member

Adds the business identity facts Polymorfa's runner needs for Business app, Official API and coexistence (hybrid) accounts, matching WhatsApp Web 2.3000.1051908825 (Cellar bundle whatsapp-1051908825).

  • Hosted pairing state. A HOSTED pair-success (account signature prefix [6,5], WAWebHandlePairSuccess:89-93) now sets store.Device.IsHosted and events.PairSuccess.IsHosted. Sqlstore upgrade v20 adds whatsmeow_device.is_hosted BOOLEAN NOT NULL DEFAULT false; existing rows read false.
  • AEA account type. types.AEAAccountType (not_set, e2ee, hybrid_e2ee, non_e2ee, WAWebAeaAccountState:6-11) parsed from w:biz business profiles (WAWebQueryBusinessProfileJob:16) and usync business results (WAWebUsyncBusiness:14). GetBusinessProfile is the reliable source; usync fills it only when the server includes it (queries unchanged).
  • Verified names. types.VerifiedName gains Serial, Issuer (smb:wa Business app, ent:wa API; WAWebValidateBusinessCertificate:6-12) and PrivacyMode. MessageInfo.BizPrivacyMode reads <biz actual_actors host_storage privacy_mode_ts> on incoming messages (WAWebHandleMsgParser:316-327), ignored for bots. GetUserInfo now returns the verified name it already parsed.
  • Hosted unavailable. events.UndecryptableMessage.IsHostedUnavailable for <unavailable hosted="true"/> (WAWebHandleMsgParser:245-248). Ack and re-request behavior unchanged.
  • Hosted Signal address. Hosted device 99 uses <lid>:99@hosted.lid, else <pn>:99@hosted (WAWebSignalAddress:14-33) for decryption, send fanout and retry receipts, distinct from a regular device 99. Previously HyperMeow encrypted to the hosted address but decrypted under the regular one. Shared JID parsing, constructors and LID-map storage are unchanged; the helper reads GetLIDForPN and uses the existing session migration.

Historical verification at c843ad5b: go build ./..., go vet ./... and go test ./... pass; go test -race on ., ./store/..., ./types/..., ./binary/.... Tests use stanza fixtures from the Cellar shapes and real in-memory Signal sessions for the hosted address (each fix was disabled in turn to confirm the test fails). Hosted send fanout keeps device 99 for 1:1 and drops it for groups. Real server delivery of E2EE traffic from :99@hosted(.lid) to companions remains unverified. Synthetic tests are not real WhatsApp acceptance.

Stacked on #53 (7c0cd0e3); retarget to main after #53 lands. Overlap with #54 is CHANGELOG.md and README.md only; WhatsApp Parity checked the file list and migration number. Tracks POL-103 under POL-101. The monorepo backend PR will pin one combined qualified commit.

Agents: owner Claude Code/T3/Anthropic claude-opus-5-5, effort high (owner, review); writer subagent Claude Code/T3/Anthropic claude-opus-5-5, effort medium, branch rajeh/wa-business-emulation-hypermeow. Provenance: transcript capture blocked (the recorder cannot read delegated T3 threads). No automated reviews requested. No merge, tag or release requested. Rollback reverts this PR and the consumer pin together.

Qualification, 2026-10-30, exact head 36a1c1e0f8cf4c25a42cd185604f75e871c79969: push CI 40981017160 passed both Go1.26 and Go1.27 jobs, including generated MEX bindings, build, full tests and pre-commit. The run's headSha was read back and matches this successor. Logs explicitly confirm TestHostedLIDTransitionMigratesEstablishedPNSession, TestHostedMigrationQueriesUseLiteralPrefixRanges and TestMigratePNToLIDUsesSQLitePrefixRange pass. This qualifies the library source for review.

The successor preserves c843ad5b and exact #53 ancestry; migration20 bytes are unchanged. It addresses two findings from the permitted automatic reaction to Ready: incoming hosted-LID traffic now migrates an established hosted-PN session, and all migration/preflight/identity/sender-key cleanup predicates use literal address-prefix ranges. The decryption regression was first reproduced locally on the predecessor, then passed in fresh successor CI. No reviewer was requested.

Limits: successor local race checks could not obtain the shared validation lease. The standalone in-memory SQLite fixture at store/sqlstore/testdata/hosted_migration_sqlite.py is included for additional verification but was not successfully executed here; its initial harness had incorrect Python parameter binding, now corrected. No real-provider or live database acceptance is claimed. Backend pinning/native acceptance and Release's merge order remain separate gates.

Continuation owner: Codex / T3 / OpenAI gpt-6.1-sol, high reasoning, existing source branch. User requested Standard service; no service-tier readback is exposed. No child or reviewer was started or resumed. No merge, tag, package publication or deployment was performed.

WhatsApp Web marks the companion as a hosted (coexistence) account when the
pair-success ADVSignedDeviceIdentityHMAC has accountType HOSTED and verifies
its HMAC with the [6,5] prefix (WAWebHandlePairSuccess:89-93,
WAWebAdvSignatureConstants:3-4, bundle whatsapp-1051908825), storing it as
isHostedMeAccount. HyperMeow verified the prefix but dropped the fact.

Add store.Device.IsHosted, set it during pairing, report it on
events.PairSuccess, and persist it in sqlstore. The account type is only
available in the pair-success container, which is not stored, so the device
table needs a column: schema v20 adds is_hosted BOOLEAN NOT NULL DEFAULT false.
Existing rows default to false and the change stays compatible with v8+.
Business profile (w:biz) and usync business results carry
<aea_account_type value="not_set|e2ee|hybrid_e2ee|non_e2ee"/> inside the
business <profile> node (WAWebQueryBusinessProfileJob:14-25,
WAWebUsyncBusiness:10-20, WAWebAeaAccountState:6-11, bundle
whatsapp-1051908825).

Add types.AEAAccountType with the four values and IsAEA, and expose it on
BusinessProfile, UserInfo and IsOnWhatsAppResponse. GetUserInfo also now
returns the verified name it already parsed, which it previously dropped.
WhatsApp Web derives isApi/isSmb from the verified name certificate issuer
(ent:wa or smb:wa), reads the serial attribute with the certificate serial as
fallback, and builds a privacy mode only when actual_actors, host_storage and
privacy_mode_ts are all present with known enum values
(WAWebValidateBusinessCertificate:6-24, WAWebCommonParsersVerifiedName:19-32,
WAWebHandleMsgTypes:2-9). Business messages carry the same privacy mode on
<biz>, ignored for bot authors, and the level and serial on the message
stanza (WAWebHandleMsgParser:309-327). Bundle whatsapp-1051908825.

Add VerifiedName.Serial, Issuer, PrivacyMode, IsAPI and IsBusinessApp, typed
issuer, actual-actor and host-storage enums, VerifiedLevel constants, and
MessageInfo.BizPrivacyMode. Existing VerifiedName fields keep their values.
<unavailable hosted="true"/> means the hosted (Cloud API) side of a
coexistence business sent a message this companion cannot decrypt; WhatsApp
Web shows a hosted-unavailable placeholder for it
(WAWebHandleMsgParser:245-248, WAWebUnavailableFanoutPlaceholderType,
bundle whatsapp-1051908825). Add UndecryptableMessage.IsHostedUnavailable.
The ack and phone re-request behavior is unchanged.
Hosted (coexistence) devices use device 99 on the hosted and hosted.lid
servers (WAWebWid:20,173-174, WAWebHandleAccountSyncNotification:47-55,
WAWebUsyncDevice:20, bundle whatsapp-1051908825). Cover usync device lists
with is_hosted, the binary AD-JID round trip, and receipts from a hosted
device. The existing handling was correct; no behavior change.
WhatsApp Web addresses a hosted device's Signal session as
<lid>:99@hosted.lid when the account's LID is known and <pn>:99@hosted
otherwise, distinct from a regular <lid>:99@lid or <pn>:99@c.us device
(WAWebSignalAddress:14-33, bundle whatsapp-1051908825).

HyperMeow normalized incoming hosted senders to s.whatsapp.net/lid before
choosing the decryption address, so a message from X:99@hosted.lid used the
regular X:99@lid session while sends to the same device used the hosted one.
Retry receipts from hosted devices had the same mismatch.

Add hostedSignalJID and rawHostedSender and use them when decrypting, in
send fanout, and when answering local and remote retry receipts. A PN hosted
address moves its sessions to the LID hosted address once the LID is known.
MessageInfo.Sender keeps its existing normalization. JID parsing, JID
constructors and LID-map storage are unchanged; the helper only reads the
existing GetLIDForPN lookup.

The test runs a real in-memory prekey exchange from X:99@hosted.lid and a
regular X:99@lid sender, a reply through encryptMessageForDevices and a
retry receipt, and checks that the two sessions stay separate.
prepareMessageNode keeps a hosted device 99 in 1:1 fanout and drops it from
group fanout (send.go). Cover both with in-memory Signal sessions so no
prekey IQ is needed.
@coderabbitai

coderabbitai Bot commented Oct 28, 2026 •

Copy link
Copy Markdown

Important

Review skipped

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

🗂️ Base branches to auto review: 1
  • dev

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
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Team
  • Run ID: c0b33e75-5f9b-4dcc-a9c1-340c15e1ea7a

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
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

@purpshell

Copy link
Copy Markdown
Member Author

Rebase sweep (2026-10-28, owner instruction "land zod changes and rebase every single PR"): no-op. Target codex/upstream-sync-20261027 (#53) is unchanged at 7c0cd0e3c1739dbc7292a822bc5dd9e6c2ce71a1, and git merge-base --is-ancestor 7c0cd0e3 c843ad5b succeeds, so this PR is already current. Head stays c843ad5b1a7d426dd680e879886e585da2e3995c; no commit, no push. Zod (tulir#862) does not affect this Go library. Checks not rerun because nothing changed.

@purpshell
purpshell marked this pull request as ready for review October 30, 2026 00:26
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 30, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-30T00:42:41.759466Z 36a1c1e Draft marked ready
🔒 Security Review ✅ Completed 2026-10-30T00:44:40.224706Z 36a1c1e Draft marked ready
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@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: c843ad5b1a

ℹ️ 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".

Comment thread hosted_signal.go Resolved
Comment thread hosted_signal.go Resolved
@purpshell
purpshell marked this pull request as draft October 30, 2026 00:31
@purpshell
purpshell marked this pull request as ready for review October 30, 2026 00:41
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.

1 participant