Repository navigation
Conversation
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.
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. 🗂️ Base branches to auto review: 1
Please check the settings in the CodeRabbit UI or the
|
|
Rebase sweep (2026-10-28, owner instruction "land zod changes and rebase every single PR"): no-op. Target |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 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".
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 bundlewhatsapp-1051908825).[6,5],WAWebHandlePairSuccess:89-93) now setsstore.Device.IsHostedandevents.PairSuccess.IsHosted. Sqlstore upgrade v20 addswhatsmeow_device.is_hosted BOOLEAN NOT NULL DEFAULT false; existing rows read false.types.AEAAccountType(not_set,e2ee,hybrid_e2ee,non_e2ee,WAWebAeaAccountState:6-11) parsed fromw:bizbusiness profiles (WAWebQueryBusinessProfileJob:16) and usync business results (WAWebUsyncBusiness:14).GetBusinessProfileis the reliable source; usync fills it only when the server includes it (queries unchanged).types.VerifiedNamegainsSerial,Issuer(smb:waBusiness app,ent:waAPI;WAWebValidateBusinessCertificate:6-12) andPrivacyMode.MessageInfo.BizPrivacyModereads<biz actual_actors host_storage privacy_mode_ts>on incoming messages (WAWebHandleMsgParser:316-327), ignored for bots.GetUserInfonow returns the verified name it already parsed.events.UndecryptableMessage.IsHostedUnavailablefor<unavailable hosted="true"/>(WAWebHandleMsgParser:245-248). Ack and re-request behavior unchanged.<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 readsGetLIDForPNand uses the existing session migration.Historical verification at
c843ad5b:go build ./...,go vet ./...andgo test ./...pass;go test -raceon.,./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 tomainafter #53 lands. Overlap with #54 isCHANGELOG.mdandREADME.mdonly; 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/Anthropicclaude-opus-5-5, effort medium, branchrajeh/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'sheadShawas read back and matches this successor. Logs explicitly confirmTestHostedLIDTransitionMigratesEstablishedPNSession,TestHostedMigrationQueriesUseLiteralPrefixRangesandTestMigratePNToLIDUsesSQLitePrefixRangepass. This qualifies the library source for review.The successor preserves
c843ad5band 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.pyis 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.