-
Notifications
You must be signed in to change notification settings - Fork 6
Implement MSC-4169: backwards-compatible send of redactions for rooms < v11 using the /send endpoint #52
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
While there is a dedicated API endpoint for redactions, being able to send redactions using the normal send endpoint is useful when using MSC-4140 for sending delayed redactions to replicate expiring messages. Currently this would only work on rooms >= v11 but fail with an internal server error on older room versions.
📝 WalkthroughSummary by CodeRabbit
WalkthroughAdds MSC4169-aware handling for redactions in create_and_send_nonmember_event (promotes or sources Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant Client
participant MessageHandler
participant AdminCheck as is_admin_redaction
Note over MessageHandler: MSC4169 experimental flag may be enabled
Client->>MessageHandler: create_and_send_nonmember_event(event_dict, ...)
alt MSC4169 enabled AND legacy redaction rules
MessageHandler->>MessageHandler: load room_version if needed\nredacts = event_dict.get("redacts") or event_dict["content"].pop("redacts", None)
MessageHandler->>AdminCheck: is_admin_redaction(redacts)
AdminCheck-->>MessageHandler: result
else MSC4169 disabled OR updated redaction rules
MessageHandler->>MessageHandler: redacts = event_dict["content"].get("redacts")
MessageHandler->>AdminCheck: is_admin_redaction(redacts)
AdminCheck-->>MessageHandler: result
end
MessageHandler-->>Client: continue with event processing/sending
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Pre-merge checks (2 passed, 1 warning)❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
Tip 👮 Agentic pre-merge checks are now available in preview!Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.
Example: reviews:
pre_merge_checks:
custom_checks:
- name: "Undocumented Breaking Changes"
mode: "warning"
instructions: |
Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal). Please share your feedback with us on this Discord post. Warning Review ran into problems🔥 ProblemsErrors were encountered while retrieving linked issues. Errors (2)
✨ Finishing Touches
🧪 Generate unit tests
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. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (1)
synapse/handlers/message.py (1)
656-663
: Suspended-user redaction path assumes content.redacts only; mirror legacy fallbackThis block fetches the target via event_dict["content"]["redacts"] and will KeyError for legacy rooms when clients send top-level redacts. Align with the new fallback used below.
Suggested change:
- event = await self.store.get_event( - event_dict["content"]["redacts"], allow_none=True - ) + redacts = event_dict.get("content", {}).get("redacts") or event_dict.get("redacts") + if not redacts: + raise SynapseError(400, "Missing redacts field", Codes.INVALID_PARAM) + event = await self.store.get_event(redacts, allow_none=True)Please add/adjust tests:
- /send redaction in rooms v1–v10 with redacts only in content → succeeds.
- Same, but only top-level redacts → still succeeds (suspended-user path included).
- v11+ continues to accept content.redacts and ignores top-level.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
synapse/handlers/message.py
(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
synapse/handlers/message.py (1)
synapse/events/__init__.py (2)
redacts
(238-242)get
(257-258)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: test-complement
- GitHub Check: test-trial
- GitHub Check: test-sytest
- GitHub Check: lint-style
- GitHub Check: lint-types
Co-authored-by: Tulir Asokan <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (4)
changelog.d/18898.feature
(1 hunks)synapse/config/experimental.py
(1 hunks)synapse/handlers/message.py
(1 hunks)synapse/rest/client/versions.py
(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- changelog.d/18898.feature
🚧 Files skipped from review as they are similar to previous changes (1)
- synapse/handlers/message.py
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: test-trial
- GitHub Check: lint-style
- GitHub Check: lint-types
- GitHub Check: test-sytest
- GitHub Check: test-complement
🔇 Additional comments (1)
synapse/config/experimental.py (1)
555-557
: LGTM: cleanly adds MSC4169 feature flag.Boolean default, naming, and placement are consistent with the rest of ExperimentalConfig.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (1)
synapse/handlers/message.py (1)
1008-1012
: Avoid KeyError on missingcontent
; use.get("content", {})
consistentlySafer and simpler; also aligns with earlier feedback.
Apply:
- if ( - event_dict["type"] == EventTypes.Redaction - and "redacts" in event_dict["content"] - and self.hs.config.experimental.msc4169_enabled - ): + if ( + event_dict["type"] == EventTypes.Redaction + and "redacts" in event_dict.get("content", {}) + and self.hs.config.experimental.msc4169_enabled + ): @@ - redacts = event_dict.get("redacts") or event_dict["content"].pop( - "redacts", None - ) + redacts = event_dict.get("redacts") or event_dict.get("content", {}).pop( + "redacts", None + ) @@ - if room_version.updated_redaction_rules: - redacts = event_dict["content"].get("redacts") + if room_version.updated_redaction_rules: + redacts = event_dict.get("content", {}).get("redacts")Also applies to: 1024-1026, 1039-1041
🧹 Nitpick comments (2)
synapse/handlers/message.py (2)
1020-1028
: Always strip duplicateredacts
from content for legacy roomsPrevents legacy validation surprises if both top-level and content copies exist.
- if not room_version.updated_redaction_rules: - # Legacy room versions need the "redacts" field outside of the event's - # content. However clients may still send it within the content, so copy - # the field if necessary for compatibility. - redacts = event_dict.get("redacts") or event_dict["content"].pop( - "redacts", None - ) - if redacts is not None and "redacts" not in event_dict: - event_dict["redacts"] = redacts + if not room_version.updated_redaction_rules: + # Legacy room versions need top-level `redacts`. Promote if needed and + # remove the duplicate from content. + content = event_dict.get("content", {}) + redacts_in_content = content.get("redacts") + top_level_redacts = event_dict.get("redacts") + redacts = top_level_redacts or redacts_in_content + if redacts is not None and "redacts" not in event_dict: + event_dict["redacts"] = redacts + # Avoid having both copies in legacy rooms. + if "redacts" in content: + content.pop("redacts", None)
1030-1047
: Computeredacts
only for redaction events in the ratelimit pathMinor clarity/perf win and avoids unnecessary dict lookups for other event types.
- if room_version.updated_redaction_rules: - redacts = event_dict.get("content", {}).get("redacts") - else: - redacts = event_dict.get("redacts") + if event_dict["type"] == EventTypes.Redaction: + if room_version.updated_redaction_rules: + redacts = event_dict.get("content", {}).get("redacts") + else: + redacts = event_dict.get("redacts") + else: + redacts = None
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
synapse/handlers/message.py
(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
synapse/handlers/message.py (4)
synapse/api/constants.py (1)
EventTypes
(107-145)synapse/events/__init__.py (6)
room_id
(230-231)room_id
(396-397)room_id
(457-458)room_id
(521-540)redacts
(238-242)get
(257-258)synapse/storage/databases/main/state.py (5)
get_room_version
(136-145)get
(994-994)get
(996-996)get
(998-998)get
(1000-1005)synapse/api/errors.py (2)
NotFoundError
(373-377)AuthError
(380-392)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: test-trial
- GitHub Check: test-sytest
- GitHub Check: test-complement
- GitHub Check: lint-types
- GitHub Check: lint-style
While there is a dedicated API endpoint for redactions, being able to send redactions using the normal send endpoint is useful when using MSC-4140 for sending delayed redactions to replicate expiring messages. Currently this would only work on rooms >= v11 but fail with an internal server error on older room versions when setting the
redacts
field in the content, since older rooms would require that field to be outside ofcontent
. We can address this by copying it over if necessary.Upstream PR: element-hq/synapse#18898