Skip to content

feat(messages): encrypted CAG reactions and channel comments, both directions - #830

Merged
jlucaso1 merged 5 commits into
mainfrom
feat/cag-enc-reactions-comments
Jun 10, 2026
Merged

feat(messages): encrypted CAG reactions and channel comments, both directions#830
jlucaso1 merged 5 commits into
mainfrom
feat/cag-enc-reactions-comments

Conversation

@jlucaso1

Copy link
Copy Markdown
Collaborator

Problem

Community Announcement Groups never accept plaintext reactions: WA Web (WAWebReactionEncryptMsgData) fetches the group metadata and, when isCag, encrypts the reaction with the target message's messageSecret and emits an enc_reaction_message { targetMessageKey, encPayload, encIv } envelope. Channel comments ship the sibling enc_comment_message envelope (WAWebSendCommentMessageAction). whatsapp-rust classified both fields for stanza typing but neither produced nor decrypted them: reactions in CAG channels silently broke in both directions, and incoming comments were dispatched as raw undecryptable envelopes. The crypto primitive (ModificationType::EncReaction / EncComment in secret_enc_addon) existed unused.

Change

Incoming (both kinds). extract_secret_encrypted now recognises the two top-level envelopes alongside secret_encrypted_message, so they flow through the entire existing machinery: store secret lookup with the LID/PN alternate, app resolver fallback, and the 4-combination identity fallback decrypt.

  • A reaction decrypts to its inner ReactionMessage (WA Web encodes only text + senderTimestampMs, confirmed in WAWebAddonEncryption's spec table: Message$ReactionMessageSpec + ENC_REACTION) and is surfaced in the plaintext-reaction shape with key filled from the envelope's target. Consumers see exactly what a plaintext reaction looks like, zero migration.
  • A comment decrypts to its inner body Message (MessageSpec + ENC_COMMENT) and is dispatched as that body. The E2E proto has no slot for the threading link, so the parent post key surfaces on the new MessageInfo::comment_target. The comment's own messageSecret is persisted under the comment's id and sender (it keys future add-ons on the comment itself), never under the parent's id.

Outgoing reactions. send_reaction now gates on WA Web's isCag (derived from default_sub_group, per WAWebGroupMetadataModel). The flag rides on the cached/persisted group metadata (GroupInfo.is_community_announce, Option<bool> so blobs persisted before the field answer "unknown" and trigger one full metadata query instead of guessing). When the chat is a CAG the reaction is encrypted and shipped as the envelope; if the parent secret was never captured the send fails with a descriptive error rather than emitting a plaintext reaction the channel drops.

Outgoing comments. New client.comments() feature handle: send_text (extended-text body, per WAWebSendCommentMessageActionUtils.encryptExtendedTextComment) and send_message for arbitrary bodies. Comments are authored under the LID identity (getMeLidUserOrThrow parity) and carry a fresh own messageSecret so they can themselves receive encrypted add-ons.

The crypto lives in wacore::reaction / wacore::comment as thin wrappers over secret_enc_addon, mirroring the existing event/poll modules. The shared outgoing resolution (parent author from the target key, secret with alternates and resolver) is one helper reused by both send paths, and the envelope key is always stamped with the same identity the HKDF was derived with, so receivers resolve exactly what we keyed.

Tests

  • wacore::reaction / wacore::comment: roundtrips, empty-text removal form, wrong-identity refusal, use-case separation (a comment payload must not decrypt under the reaction use-case), secret-size validation.
  • features::message_edit: envelope recognition for both kinds, malformed-envelope rejection (bad IV, missing key), per-kind dispatch through the public fallback API (primary PN fails, LID fallback decrypts).
  • Pipeline tests against the real receive path: an encrypted reaction decrypts through maybe_decrypt_secret_encrypted_message with the key filled from the envelope; an encrypted comment goes through dispatch_parsed_message end to end, asserting the dispatched Event::Message carries the body, comment_target on the info, the envelope stripped, and the comment's secret persisted under the comment's own id while the parent's row stays untouched.
  • Full workspace suite green (2139 tests), cargo clippy --all-targets -- -D warnings clean.

Breaking

MessageInfo gains the comment_target: Option<MessageKey> field (struct literal constructors need the field or ..Default::default()); GroupInfo gains is_community_announce: Option<bool> (serde-compatible with old persisted blobs); SecretEncKind gains the EncReaction/EncComment variants. Pre-1.0 additive surface.

…in both directions

Community Announcement Groups reject plaintext reactions: WA Web encrypts
them with the target's messageSecret (WAWebReactionEncryptMsgData) and
ships an enc_reaction_message envelope. Channel comments use the sibling
enc_comment_message envelope. We classified both for send but never
produced or decrypted either, so reactions in CAG channels silently broke
in both directions and incoming comments surfaced as unreadable
envelopes.

Incoming: extract_secret_encrypted now recognises the two top-level
envelopes alongside secret_encrypted_message, reusing the whole existing
secret-lookup and LID/PN fallback machinery. A reaction decrypts to its
inner ReactionMessage (WA Web encodes only text + senderTimestampMs) and
is surfaced in the plaintext-reaction shape with the key filled from the
envelope, so consumers see no difference from a normal reaction. A
comment decrypts to its inner body Message; the parent post key surfaces
on the new MessageInfo::comment_target, and the comment's own secret is
persisted under the comment's id and sender, never the parent's.

Outgoing: send_reaction now gates on WA Web's isCag (default_sub_group;
the flag is carried on the cached group metadata, with a one-time full
metadata query for blobs persisted before the field existed) and emits
the encrypted envelope, failing loudly when the parent secret was never
captured instead of sending a plaintext reaction the channel drops. A new
Comments feature handle authors comments (extended-text body per
WAWebSendCommentMessageActionUtils, LID identity, fresh own secret so the
comment can itself receive add-ons).

The crypto lives in wacore::reaction / wacore::comment as thin wrappers
over secret_enc_addon, mirroring the existing event/poll modules.
@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@jlucaso1, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 31 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 37ebe295-0938-443d-b95f-39ea4b38c560

📥 Commits

Reviewing files that changed from the base of the PR and between 885cf94 and 7a69d61.

📒 Files selected for processing (2)
  • src/features/message_edit.rs
  • src/features/reaction.rs
📝 Walkthrough

Walkthrough

Adds encrypted comment and reaction addon support for Community Announcement Groups: crypto helpers, message extraction/decryption for enc_reaction/enc_comment, inbound persistence/dispatch updates (comment threading), outbound Comments API, and CAG-aware encrypted reaction sending.

Changes

Encrypted Comments and Reactions for Community Announcement Groups

Layer / File(s) Summary
Crypto primitives: encrypt/decrypt for reactions and comments
wacore/src/reaction.rs, wacore/src/comment.rs, wacore/src/lib.rs
New wacore::reaction and wacore::comment modules provide symmetric encrypt/decrypt functions using the addon framework; validate 32-byte secrets, derive addon contexts by parent+identity, and include unit tests.
Type contracts: GroupInfo and MessageInfo metadata
wacore/src/client/context.rs, wacore/src/types/message.rs, src/features/groups.rs
Adds GroupInfo.is_community_announce optional flag (deserialized, set during group query) and MessageInfo.comment_target optional field to link decrypted comment bodies to parent message keys.
Secret-encrypted addon extraction/decryption
src/features/message_edit.rs
Extends SecretEncKind with EncReaction/EncComment; extract_secret_encrypted recognizes enc_reaction_message/enc_comment_message; decrypt_secret_encrypted dispatches to wacore::reaction/wacore::comment decryptors for those kinds; fallback logic adapts for addon kinds. Tests added in enc_addon_tests.
Inbound decryption, dispatch, and persistence
src/message/msg_secret.rs, src/message/dispatch.rs, src/message/tests.rs, src/pdo.rs
Binds decrypted inner message mutable to populate reaction key/comment secret, copies outer comment secret into inner message_context when needed, refactors secret re-persist parameter derivation by SecretEncKind, exposes addon-self/resolve helpers, propagates comment_target at dispatch, and adds inbound tests verifying decrypt+dispatch and persistence behavior.
Outbound Comments API
src/features/comments.rs, src/features/mod.rs
Adds Comments<'a> and Client::comments(). send_text builds extended-text message; send_message resolves parent author/secret, selects commenter identity (LID with PN fallback), encrypts via wacore::comment::encrypt_comment_with_secret, sets parent_key.participant when missing, attaches fresh 32-byte comment secret in message_context_info.message_secret, sends enc_comment_message, and persists the comment secret for outbound add-ons. Module declared and re-exported.
Outbound reaction encryption for CAG
src/features/reaction.rs
send_reaction detects Community Announcement Group status and routes to send_enc_reaction when appropriate. send_enc_reaction resolves the parent author/secret, encrypts reaction with wacore::reaction::encrypt_reaction_with_secret, ensures target_key.participant for receivers, constructs enc_reaction_message, and sends.
Module export formatting
src/lib.rs
Reformatted pub use features::{ ... } list by line-wrapping only (no semantic change).

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant CommentsAPI
  participant MsgSecretResolver
  participant CommentCrypto
  participant ClientSend

  User->>CommentsAPI: send_message(chat, parent_key, body)
  CommentsAPI->>MsgSecretResolver: resolve_outgoing_addon_parent(chat, parent_key)
  MsgSecretResolver-->>CommentsAPI: parent_jid + parent_secret
  CommentsAPI->>CommentCrypto: encrypt_comment_with_secret(inner, parent_secret, parent_id, parent_jid, commenter_jid)
  CommentCrypto-->>CommentsAPI: enc_payload + iv
  CommentsAPI->>ClientSend: send_message(enc_comment_message with message_secret)
  ClientSend->>ClientSend: persist_outbound_msg_secret(comment_id, comment_secret)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main feature: encrypted Community Announcement Group reactions and comments with bidirectional support.
Description check ✅ Passed The description thoroughly explains the problem, changes, test coverage, and breaking changes, directly aligned with the changeset.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/cag-enc-reactions-comments

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

github-actions Bot commented Jun 10, 2026

Copy link
Copy Markdown

Benchmark Results

67 unchanged benchmark(s)
Benchmark Current Baseline Change
reporting_token_benchmark::content_extraction_group::bench_content_extraction simple:setup_simple_message() 2,927 2,927 +0.0%
reporting_token_benchmark::content_extraction_group::bench_content_extraction extended:setup_extended_message() 8,448 8,448 +0.0%
reporting_token_benchmark::key_derivation_group::bench_key_derivation 31,317 31,317 +0.0%
reporting_token_benchmark::token_calculation_group::bench_token_calculation 13,827 13,827 +0.0%
reporting_token_benchmark::full_generation_group::bench_full_token_generation simple:setup_full_gen_simple() 49,487 49,487 +0.0%
reporting_token_benchmark::full_generation_group::bench_full_token_generation extended:setup_full_gen_extended() 55,003 55,003 +0.0%
reporting_token_benchmark::message_encoding_group::bench_message_encoding simple:setup_simple_message() 1,679 1,679 +0.0%
reporting_token_benchmark::message_encoding_group::bench_message_encoding extended:setup_extended_message() 4,393 4,393 +0.0%
send_receive_benchmark::dm_send::bench_dm_send text:setup_dm_send() 113,165 113,303 -0.1%
send_receive_benchmark::dm_recv::bench_dm_recv text:setup_dm_recv() 1,656,619 1,656,625 -0.0%
send_receive_benchmark::group_send::bench_group_send group_10:setup_group_send_10() 641,922 641,918 +0.0%
send_receive_benchmark::group_send::bench_group_send group_50:setup_group_send_50() 852,906 853,211 -0.0%
send_receive_benchmark::group_send::bench_group_send group_256:setup_group_send_256() 1,989,791 1,989,742 +0.0%
send_receive_benchmark::group_send_skdm::bench_group_send_skdm skdm_10:setup_group_skdm_10() 734,151 734,146 +0.0%
send_receive_benchmark::group_send_skdm::bench_group_send_skdm skdm_50:setup_group_skdm_50() 1,288,099 1,288,151 -0.0%
send_receive_benchmark::group_send_skdm::bench_group_send_skdm skdm_256:setup_group_skdm_256() 4,172,273 4,154,983 +0.4%
send_receive_benchmark::group_recv::bench_group_recv text:setup_group_recv() 514,512 520,001 -1.1%
binary_benchmark::marshal_group::bench_marshal_allocating 40,690 40,690 +0.0%
binary_benchmark::marshal_group::bench_marshal_auto_allocating 40,743 40,743 +0.0%
binary_benchmark::marshal_group::bench_marshal_exact_allocating 61,909 61,909 +0.0%
binary_benchmark::marshal_group::bench_marshal_reusing_buffer 38,953 38,953 +0.0%
binary_benchmark::marshal_group::bench_marshal_reusing_buffer_vec_writer 40,796 40,796 +0.0%
binary_benchmark::marshal_group::bench_marshal_long_string 5,144 5,144 +0.0%
binary_benchmark::marshal_group::bench_marshal_auto_long_string 5,174 5,174 +0.0%
binary_benchmark::marshal_group::bench_marshal_exact_long_string 6,954 6,954 +0.0%
binary_benchmark::marshal_group::bench_marshal_huge_bytes_allocating 528,339 528,339 +0.0%
binary_benchmark::marshal_group::bench_marshal_auto_huge_bytes_allocating 527,963 527,963 +0.0%
binary_benchmark::marshal_group::bench_marshal_exact_huge_bytes_allocating 529,211 529,211 +0.0%
binary_benchmark::marshal_group::bench_marshal_many_children_allocating 4,769,980 4,769,980 +0.0%
binary_benchmark::marshal_group::bench_marshal_auto_many_children_allocating 4,769,621 4,769,621 +0.0%
binary_benchmark::marshal_group::bench_marshal_exact_many_children_allocating 12,643,701 12,643,701 +0.0%
binary_benchmark::unmarshal_group::bench_unmarshal small:setup_small_marshaled() 1,850 1,850 +0.0%
binary_benchmark::unmarshal_group::bench_unmarshal large:setup_large_marshaled() 28,069 28,069 +0.0%
binary_benchmark::unpack_group::bench_unpack_uncompressed 618 618 +0.0%
binary_benchmark::unpack_group::bench_unpack_compressed 672,888 672,888 +0.0%
binary_benchmark::attr_parser_group::bench_attr_parser attr_lookup:setup_attr_marshaled() 3,736 3,736 +0.0%
binary_benchmark::roundtrip_group::bench_roundtrip small:setup_small_marshaled() 3,845 3,845 +0.0%
binary_benchmark::roundtrip_group::bench_roundtrip large:setup_large_marshaled() 47,180 47,180 +0.0%
binary_benchmark::roundtrip_group::bench_roundtrip_auto small:setup_small_marshaled() 3,871 3,871 +0.0%
binary_benchmark::roundtrip_group::bench_roundtrip_auto large:setup_large_marshaled() 47,241 47,241 +0.0%
binary_benchmark::roundtrip_group::bench_roundtrip_exact small:setup_small_marshaled() 5,206 5,206 +0.0%
binary_benchmark::roundtrip_group::bench_roundtrip_exact large:setup_large_marshaled() 65,610 65,610 +0.0%
binary_benchmark::child_iteration_group::bench_get_children_by_tag 234,591 234,591 +0.0%
binary_benchmark::jid_optimization_group::bench_jid_to_owned_access jid_access:setup_jid_heavy_marshaled() 8,579 8,579 +0.0%
numeric_attr_benchmark::bench_group::bench_baseline_u32 254 254 +0.0%
numeric_attr_benchmark::bench_group::bench_proposed_u32 91 91 +0.0%
numeric_attr_benchmark::bench_group::bench_baseline_u64 292 292 +0.0%
numeric_attr_benchmark::bench_group::bench_proposed_u64 137 137 +0.0%
numeric_attr_benchmark::bench_group::bench_baseline_i64 317 317 +0.0%
numeric_attr_benchmark::bench_group::bench_proposed_i64 145 145 +0.0%
numeric_attr_benchmark::bench_group::bench_baseline_loop_100_u64 27,425 27,425 +0.0%
numeric_attr_benchmark::bench_group::bench_proposed_loop_100_u64 10,725 10,725 +0.0%
libsignal_benchmark::dm_group::bench_dm_session_establishment setup:setup_dm_users() 4,143,909 4,141,782 +0.1%
libsignal_benchmark::dm_group::bench_dm_encrypt_first_message first_msg:setup_dm_session() 100,133 100,131 +0.0%
libsignal_benchmark::dm_group::bench_dm_decrypt_first_message decrypt_prekey:setup_dm_with_first_message() 4,264,189 4,264,189 +0.0%
libsignal_benchmark::dm_group::bench_dm_encrypt_subsequent_message subsequent:setup_established_dm_session() 100,399 100,399 +0.0%
libsignal_benchmark::group_messaging_group::bench_group_create_distribution_message create:setup_group_sender() 210,262 210,262 +0.0%
libsignal_benchmark::group_messaging_group::bench_group_encrypt_message encrypt:setup_group_with_distribution() 496,921 496,921 +0.0%
libsignal_benchmark::group_messaging_group::bench_group_decrypt_message decrypt:setup_group_with_encrypted_message() 509,990 507,443 +0.5%
libsignal_benchmark::conversation_group::bench_full_dm_conversation full:setup_conversation_data() 11,978,961 11,978,335 +0.0%
libsignal_benchmark::signature_group::bench_signature_creation sign:setup_keypair_with_message() 2,466,138 2,466,138 +0.0%
libsignal_benchmark::signature_group::bench_signature_verification verify:setup_keypair_with_message() 4,834,682 4,845,642 -0.2%
libsignal_benchmark::signature_group::bench_key_generation keygen 2,043,351 2,043,351 +0.0%
libsignal_benchmark::session_optimization_group::bench_decrypt_with_previous_session previous_session:setup_with_archived_sessions() 37,404 37,414 -0.0%
libsignal_benchmark::session_optimization_group::bench_out_of_order_decryption out_of_order:setup_out_of_order_messages() 3,617,967 3,617,967 +0.0%
libsignal_benchmark::session_optimization_group::bench_promote_matching_session promote:setup_promote_matching_session() 230,638 230,648 -0.0%
libsignal_benchmark::session_optimization_group::bench_message_key_eviction eviction:setup_message_key_eviction() 9,980,959 9,980,959 +0.0%
No significant changes detected.

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

ℹ️ 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 src/message/dispatch.rs

@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/message/tests.rs`:
- Around line 9705-9733: The test uses a fixed tokio::time::sleep + try_recv
loop which can race; replace that pattern with the deterministic helper
collect_event(...) to poll rx with a bounded timeout after calling
client.dispatch_parsed_message(msg, &info).await; use collect_event(rx, timeout)
(or the project's equivalent) to retrieve the Event::Message and then assert the
same conditions on the received msg/info (matching COMMENT_ID, checking
extended_text_message text == "great post", enc_comment_message is None, and
info.comment_target id == PARENT_ID). Keep the same assertions but remove the
sleep and manual try_recv loop so the test is deterministic on slow CI.
🪄 Autofix (Beta)

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: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 8b6c019d-2d60-487c-8c81-1a7138494499

📥 Commits

Reviewing files that changed from the base of the PR and between 3166628 and d250ed4.

📒 Files selected for processing (15)
  • src/features/comments.rs
  • src/features/groups.rs
  • src/features/message_edit.rs
  • src/features/mod.rs
  • src/features/reaction.rs
  • src/lib.rs
  • src/message/dispatch.rs
  • src/message/msg_secret.rs
  • src/message/tests.rs
  • src/pdo.rs
  • wacore/src/client/context.rs
  • wacore/src/comment.rs
  • wacore/src/lib.rs
  • wacore/src/reaction.rs
  • wacore/src/types/message.rs

Comment thread src/message/tests.rs
…tched body

The comment's own messageSecret rides the OUTER envelope (WA Web puts it
on the comment msgData), which substitution dropped, so app-managed
secret storage (Disabled policy) could never learn it for add-ons
targeting the comment. Copy it onto the dispatched body when the inner
carries none. The pipeline test now ships the secret in the WA Web shape
(outer envelope) and asserts it survives substitution, and polls with a
deadline instead of a fixed sleep.

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

ℹ️ 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 src/features/comments.rs Outdated
Comment thread src/message/msg_secret.rs

@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/message/msg_secret.rs`:
- Around line 344-349: The current block only sets inner.message_context_info
when it is None, but if inner.message_context_info exists without a
message_secret we must merge the outer envelope's comment secret into it; update
the logic inside the env.kind == SecretEncKind::EncComment guard to: if
msg.message_context_info.as_ref() yields outer_mci with message_secret, then if
inner.message_context_info is None set inner.message_context_info =
Some(outer_mci.clone()), else if
inner.message_context_info.as_mut().message_secret.is_none() copy
outer_mci.message_secret.clone() into
inner.message_context_info.as_mut().message_secret so the comment secret is
preserved for re-persist. Ensure you reference env.kind,
SecretEncKind::EncComment, inner.message_context_info, msg.message_context_info,
outer_mci and message_secret when making the change.
🪄 Autofix (Beta)

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: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: c2adcb89-d68c-426e-9186-9328a02610a4

📥 Commits

Reviewing files that changed from the base of the PR and between d250ed4 and d71e1d3.

📒 Files selected for processing (2)
  • src/message/msg_secret.rs
  • src/message/tests.rs

Comment thread src/message/msg_secret.rs Outdated
…for CAG addons

Three review findings on the addon paths. The outer comment secret now
merges into an existing secret-less inner context instead of only filling
a missing one. A from_me target key in a group resolves our identity from
the group's addressing mode (outbound group secrets are persisted under
the group sender identity, and WA Web authors CAG addons under LID), not
from the chat server. And an outbound comment persists its own minted
secret under the commenter identity and sent id, since the send path only
persists reporting-token secrets; without it, add-ons targeting our own
comment could never be decrypted.

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/features/comments.rs (1)

91-96: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Refactor to generate secret directly as [u8; 32] to avoid .expect().

Per our coding guidelines, .expect() shouldn't be used outside tests. Yes, the Vec is provably 32 bytes, but why create a Vec just to convert it? Generate the array directly — cleaner and zero-cost.

-        let comment_secret: Vec<u8> = {
-            use rand::Rng;
-            let mut secret = vec![0u8; 32];
-            rand::make_rng::<rand::rngs::StdRng>().fill_bytes(&mut secret);
-            secret
-        };
+        let comment_secret: [u8; 32] = {
+            use rand::Rng;
+            let mut secret = [0u8; 32];
+            rand::make_rng::<rand::rngs::StdRng>().fill_bytes(&mut secret);
+            secret
+        };

Then line 105 becomes comment_secret.to_vec() and lines 115-118 simplify to just &comment_secret.

Also applies to: 115-118

🤖 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/features/comments.rs` around lines 91 - 96, Replace the Vec-based secret
with a fixed-size array by creating comment_secret as a [u8; 32] and filling it
from the RNG (e.g., use rand::rngs::StdRng and RngCore::fill_bytes on a &mut
[u8;32]) instead of building a Vec and calling .try_into(). Update subsequent
uses: where you previously converted the Vec, call comment_secret.to_vec(), and
where you passed a slice or reference, pass &comment_secret (or
&comment_secret[..]) so no .expect() or unnecessary allocation/conversion is
required; adjust any function signatures or borrows that expected Vec<u8>
accordingly.

Source: Coding guidelines

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

Outside diff comments:
In `@src/features/comments.rs`:
- Around line 91-96: Replace the Vec-based secret with a fixed-size array by
creating comment_secret as a [u8; 32] and filling it from the RNG (e.g., use
rand::rngs::StdRng and RngCore::fill_bytes on a &mut [u8;32]) instead of
building a Vec and calling .try_into(). Update subsequent uses: where you
previously converted the Vec, call comment_secret.to_vec(), and where you passed
a slice or reference, pass &comment_secret (or &comment_secret[..]) so no
.expect() or unnecessary allocation/conversion is required; adjust any function
signatures or borrows that expected Vec<u8> accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 7a991bae-2ff2-4b5e-a55d-7a574dd43c83

📥 Commits

Reviewing files that changed from the base of the PR and between d71e1d3 and 885cf94.

📒 Files selected for processing (3)
  • src/features/comments.rs
  • src/message/msg_secret.rs
  • src/message/tests.rs

@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: 885cf949a0

ℹ️ 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 src/features/message_edit.rs Outdated
…lback helper

The public decrypt_secret_encrypted_with_fallback only attempted the
combined fallback pair for the reaction/comment kinds, while a migration
case can need the alternate identity on only one side of the HKDF. It
now tries the mixed combinations too, deduplicated and short-circuiting
on success, matching what the receive path already does. Test covers the
fallback-modifier-only combination.

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

ℹ️ 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 src/features/reaction.rs
Receivers derive the addon key with the stanza sender, which in a CAG is
our LID regardless of the parent author's namespace, so keying the HKDF
off the author (a PN participant in a legacy or cross-addressed key)
produced reactions other clients could not open. Mirror the comment
path: own LID, PN only when no LID is known.
@jlucaso1
jlucaso1 merged commit 2b2d774 into main Jun 10, 2026
12 checks passed
@jlucaso1
jlucaso1 deleted the feat/cag-enc-reactions-comments branch June 10, 2026 14:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant