fix: add pagination and validation to messages query to prevent memory exhaustion#111
Conversation
WalkthroughGroup message retrieval was converted to a paginated API: a public Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
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.
Actionable comments posted: 1
🧹 Nitpick comments (1)
crates/mdk-sqlite-storage/src/groups.rs (1)
378-380: Consider moving imports to the top of the test module.Per coding guidelines,
usestatements should be placed at the top of the module scope rather than inside functions. These imports could be moved alongside the existing imports at lines 319-322.🔎 Suggested refactor
#[cfg(test)] mod tests { use mdk_storage_traits::groups::types::GroupState; + use mdk_storage_traits::messages::MessageStorage; + use mdk_storage_traits::messages::types::{Message, MessageState}; + use nostr::{EventId, Kind, PublicKey, Tags, Timestamp, UnsignedEvent}; use mdk_storage_traits::test_utils::crypto_utils::generate_random_bytes; use super::*; // ... existing tests ... #[test] fn test_messages_pagination() { - use mdk_storage_traits::messages::MessageStorage; - use mdk_storage_traits::messages::types::{Message, MessageState}; - use nostr::{EventId, Kind, PublicKey, Tags, Timestamp, UnsignedEvent}; - let storage = MdkSqliteStorage::new_in_memory().unwrap();
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
crates/mdk-memory-storage/CHANGELOG.mdcrates/mdk-memory-storage/src/groups.rscrates/mdk-sqlite-storage/CHANGELOG.mdcrates/mdk-sqlite-storage/src/groups.rscrates/mdk-storage-traits/CHANGELOG.mdcrates/mdk-storage-traits/src/groups/mod.rs
🧰 Additional context used
📓 Path-based instructions (3)
crates/*/CHANGELOG.md
📄 CodeRabbit inference engine (AGENTS.md)
crates/*/CHANGELOG.md: Every change that modifies functionality must update the appropriate crate CHANGELOG.md file (mdk-core, mdk-storage-traits, mdk-memory-storage, or mdk-sqlite-storage)
Follow Keep a Changelog format with sections for Unreleased, Breaking changes, Changed, Added, Fixed, Removed, and Deprecated in CHANGELOG files
Always include a PR reference link in changelog entries using the format([#123](https://github.com/marmot-protocol/mdk/pull/123))
Files:
crates/mdk-memory-storage/CHANGELOG.mdcrates/mdk-storage-traits/CHANGELOG.mdcrates/mdk-sqlite-storage/CHANGELOG.md
**/*.md
📄 CodeRabbit inference engine (AGENTS.md)
Never use bare URLs in markdown files. All URLs must be properly formatted: use
[link text](url)for inline links,<url>for standalone URLs,([#123](https://github.com/marmot-protocol/mdk/pull/123))for PR/issue references, and([abc1234](https://github.com/.../commit/abc1234...))for commit references
Files:
crates/mdk-memory-storage/CHANGELOG.mdcrates/mdk-storage-traits/CHANGELOG.mdcrates/mdk-sqlite-storage/CHANGELOG.md
**/*.rs
📄 CodeRabbit inference engine (.cursor/rules/rust.mdc)
**/*.rs: All trait bounds should be written inwhereclauses rather than inline with type parameters in Rust
UseSelfinstead of the explicit type name when referring to the type in impl blocks
DeriveDebug,Clone,Copy,PartialEq,Eq, andHashfor public types when possible, in that order
DeriveDefaultfor public types when there is a reasonable default value
Always use full paths for logging withtracing::<op>!(...)instead of importing and using<op>!(...)
Prefer.to_string()or.to_owned()for&strtoStringconversion instead of.into()orString::from()
Order imports in the following sequence: (1) core/alloc/std, (2) external crates, (3) current sub-modules with mod declarations, (4) internal crate modules
Import thefmtmodule rather than using full paths when implementing traits fromcore::fmtorstd::fmt
Use relativeself::imports for sub-modules rather than bare module names
Place allusestatements at the top of the file following the specified import order, never inside functions, methods, or other blocks
Avoidif let ... { } else { }constructs; usematchinstead when both branches are non-empty
Useif let ... { }without an else clause when a match arm would be intentionally empty
Define sub-modules using separate files (e.g.,x.rs) withmod x;declaration rather than inlinemod x { .. }blocks, except fortestsandbenchesmodules
Use inlinemod tests { #[cfg(test)] ... }for test modules rather than creating separatetests.rsfiles
Use inlinemod benches { #[cfg(bench)] ... }for benchmark modules rather than creating separatebenches.rsfiles
**/*.rs: All trait bounds must be placed inwhereclauses, not inline with generic parameters in Rust code
UseSelfinstead of the type name when possible in Rust code
Derive macros must be ordered as:Debug,Clone,Copy,PartialEq,Eq,Hashin Rust code
Always usetracing::warn!(...)with explicit module path, never im...
Files:
crates/mdk-sqlite-storage/src/groups.rscrates/mdk-storage-traits/src/groups/mod.rscrates/mdk-memory-storage/src/groups.rs
🧠 Learnings (2)
📚 Learning: 2026-01-01T16:28:05.759Z
Learnt from: CR
Repo: marmot-protocol/mdk PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-01T16:28:05.759Z
Learning: Applies to crates/*/CHANGELOG.md : Every change that modifies functionality must update the appropriate crate CHANGELOG.md file (mdk-core, mdk-storage-traits, mdk-memory-storage, or mdk-sqlite-storage)
Applied to files:
crates/mdk-memory-storage/CHANGELOG.mdcrates/mdk-storage-traits/CHANGELOG.mdcrates/mdk-storage-traits/src/groups/mod.rscrates/mdk-sqlite-storage/CHANGELOG.md
📚 Learning: 2026-01-01T15:51:58.505Z
Learnt from: dannym-arx
Repo: marmot-protocol/mdk PR: 98
File: crates/mdk-core/CHANGELOG.md:46-46
Timestamp: 2026-01-01T15:51:58.505Z
Learning: In CHANGELOG.md files, keep the GitHub issue references (e.g., #61) as-is; do not convert '#61' (with no space) into an MD heading '# 61'. The hash without a space is intentional for GitHub auto-linking. This guideline applies to all CHANGELOG.md files in the repo.
Applied to files:
crates/mdk-memory-storage/CHANGELOG.mdcrates/mdk-storage-traits/CHANGELOG.mdcrates/mdk-sqlite-storage/CHANGELOG.md
🧬 Code graph analysis (1)
crates/mdk-sqlite-storage/src/groups.rs (5)
crates/mdk-memory-storage/src/groups.rs (2)
messages_paginated(63-101)messages(54-61)crates/mdk-storage-traits/src/groups/mod.rs (2)
messages_paginated(86-91)messages(50-50)crates/mdk-storage-traits/src/group_id.rs (2)
as_slice(16-18)from_slice(11-13)crates/mdk-sqlite-storage/src/db.rs (5)
row_to_message(205-250)from(24-26)from(30-32)from(66-68)from(72-74)crates/mdk-storage-traits/src/test_utils.rs (1)
generate_random_bytes(8-13)
🔇 Additional comments (13)
crates/mdk-storage-traits/CHANGELOG.md (1)
34-34: LGTM on added entries for pagination support.Lines 34 and 39 properly document the new
messages_paginated()method and the security fix with correct PR references and markdown formatting.Also applies to: 39-39
crates/mdk-memory-storage/CHANGELOG.md (1)
34-34: LGTM on pagination implementation entries.Both entries properly document the implementation of
messages_paginated()and the security fix with correct PR references and markdown formatting.Also applies to: 38-38
crates/mdk-sqlite-storage/CHANGELOG.md (1)
34-34: LGTM on SQLite pagination implementation entries.Both entries properly document the implementation of
messages_paginated()and the security fix with correct PR references and markdown formatting.Also applies to: 38-38
crates/mdk-sqlite-storage/src/groups.rs (3)
127-134: LGTM!The delegation pattern is clean and maintains backward compatibility by using the default limit and offset of 0.
136-182: LGTM!The implementation correctly:
- Validates limit bounds before querying
- Checks group existence with a clear error message
- Uses parameterized SQL with
LIMIT ? OFFSET ?preventing SQL injection- Orders by
created_at DESCconsistent with the memory storage implementation- Safely casts
usizetoi64(well within bounds given MAX_MESSAGE_LIMIT = 10000)
433-488: Good test coverage for pagination edge cases.The tests thoroughly cover:
- Pagination boundaries (pages 1, 2, 3 with proper offsets)
- Ordering verification (newest first)
- Non-overlapping pages
- Offset beyond available data returning empty
- Invalid limit values (0 and exceeding MAX)
- Non-existent group error handling
crates/mdk-storage-traits/src/groups/mod.rs (3)
22-26: LGTM! Well-chosen default values.The constants are appropriately sized:
DEFAULT_MESSAGE_LIMIT = 1000provides a reasonable default for typical use casesMAX_MESSAGE_LIMIT = 10000prevents memory exhaustion while allowing flexibilityThese values align with the PR objective of preventing denial-of-service through unbounded queries.
46-50: Good addition of the memory warning.The warning clearly communicates the risk and directs users to the safer paginated alternative.
52-91: Excellent documentation for the new trait method.The doc comments are comprehensive, covering:
- Clear parameter descriptions with validation rules
- Return value semantics (newest first ordering)
- All error conditions documented
- Practical usage example
This provides excellent guidance for implementers and consumers of this trait.
crates/mdk-memory-storage/src/groups.rs (4)
54-61: LGTM!Consistent with the SQLite implementation - delegates to the paginated version with default limit.
88-96: LGTM!The pagination logic is correct:
start = offset.min(messages.len())- safely handles offset beyond array boundsend = (offset + limit).min(messages.len())- safely clamps to available messages- Slicing
[start..end]returns empty whenstart == end(offset beyond data)The in-memory sort is necessary since LRU cache doesn't maintain order, and this aligns with the SQLite
ORDER BY created_at DESC.
189-196: Good test module structure.The test module correctly follows the coding guidelines:
- Inline
mod testswith#[cfg(test)]as required- Imports placed at the top of the module scope
198-337: Comprehensive test coverage.The test thoroughly validates:
- Default limit behavior
- Pagination across multiple pages
- Descending order by
created_at- Non-overlapping pages
- Offset beyond available data
- Invalid limit values (0 and exceeding MAX)
- Non-existent group error
- Empty group with no messages
This provides excellent confidence in the implementation correctness.
✅ Coverage: 86.35% → 86.55% (+0.20%) |
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
crates/mdk-storage-traits/CHANGELOG.md (1)
35-35: PR reference is missing (already flagged in previous review).This line is missing the required PR reference link that should be appended in the format
([#111](https://github.com/marmot-protocol/mdk/pull/111)).
🧹 Nitpick comments (1)
crates/mdk-sqlite-storage/src/groups.rs (1)
159-205: Consider validating the offset parameter.While the
limitparameter is properly validated (lines 166-172), theoffsetparameter is not. Although passing excessively large offset values would simply return an empty result set rather than cause errors, adding validation would make the API more robust and provide clearer feedback to callers.🔎 Proposed validation for offset parameter
) -> Result<Vec<Message>, GroupError> { // Validate limit is within allowed range if !(1..=mdk_storage_traits::groups::MAX_MESSAGE_LIMIT).contains(&limit) { return Err(GroupError::InvalidParameters(format!( "Limit must be between 1 and {}, got {}", mdk_storage_traits::groups::MAX_MESSAGE_LIMIT, limit ))); } + + // Validate offset is reasonable + if offset > 1_000_000 { + return Err(GroupError::InvalidParameters(format!( + "Offset {} exceeds reasonable maximum", + offset + ))); + } // First verify the group exists
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
crates/mdk-memory-storage/CHANGELOG.mdcrates/mdk-sqlite-storage/CHANGELOG.mdcrates/mdk-sqlite-storage/src/groups.rscrates/mdk-storage-traits/CHANGELOG.md
✅ Files skipped from review due to trivial changes (1)
- crates/mdk-memory-storage/CHANGELOG.md
🚧 Files skipped from review as they are similar to previous changes (1)
- crates/mdk-sqlite-storage/CHANGELOG.md
🧰 Additional context used
📓 Path-based instructions (3)
crates/*/CHANGELOG.md
📄 CodeRabbit inference engine (AGENTS.md)
crates/*/CHANGELOG.md: Every change that modifies functionality must update the appropriate crate CHANGELOG.md file (mdk-core, mdk-storage-traits, mdk-memory-storage, or mdk-sqlite-storage)
Follow Keep a Changelog format with sections for Unreleased, Breaking changes, Changed, Added, Fixed, Removed, and Deprecated in CHANGELOG files
Always include a PR reference link in changelog entries using the format([#123](https://github.com/marmot-protocol/mdk/pull/123))
Files:
crates/mdk-storage-traits/CHANGELOG.md
**/*.md
📄 CodeRabbit inference engine (AGENTS.md)
Never use bare URLs in markdown files. All URLs must be properly formatted: use
[link text](url)for inline links,<url>for standalone URLs,([#123](https://github.com/marmot-protocol/mdk/pull/123))for PR/issue references, and([abc1234](https://github.com/.../commit/abc1234...))for commit references
Files:
crates/mdk-storage-traits/CHANGELOG.md
**/*.rs
📄 CodeRabbit inference engine (.cursor/rules/rust.mdc)
**/*.rs: All trait bounds should be written inwhereclauses rather than inline with type parameters in Rust
UseSelfinstead of the explicit type name when referring to the type in impl blocks
DeriveDebug,Clone,Copy,PartialEq,Eq, andHashfor public types when possible, in that order
DeriveDefaultfor public types when there is a reasonable default value
Always use full paths for logging withtracing::<op>!(...)instead of importing and using<op>!(...)
Prefer.to_string()or.to_owned()for&strtoStringconversion instead of.into()orString::from()
Order imports in the following sequence: (1) core/alloc/std, (2) external crates, (3) current sub-modules with mod declarations, (4) internal crate modules
Import thefmtmodule rather than using full paths when implementing traits fromcore::fmtorstd::fmt
Use relativeself::imports for sub-modules rather than bare module names
Place allusestatements at the top of the file following the specified import order, never inside functions, methods, or other blocks
Avoidif let ... { } else { }constructs; usematchinstead when both branches are non-empty
Useif let ... { }without an else clause when a match arm would be intentionally empty
Define sub-modules using separate files (e.g.,x.rs) withmod x;declaration rather than inlinemod x { .. }blocks, except fortestsandbenchesmodules
Use inlinemod tests { #[cfg(test)] ... }for test modules rather than creating separatetests.rsfiles
Use inlinemod benches { #[cfg(bench)] ... }for benchmark modules rather than creating separatebenches.rsfiles
**/*.rs: All trait bounds must be placed inwhereclauses, not inline with generic parameters in Rust code
UseSelfinstead of the type name when possible in Rust code
Derive macros must be ordered as:Debug,Clone,Copy,PartialEq,Eq,Hashin Rust code
Always usetracing::warn!(...)with explicit module path, never im...
Files:
crates/mdk-sqlite-storage/src/groups.rs
🧠 Learnings (3)
📚 Learning: 2026-01-01T16:28:05.759Z
Learnt from: CR
Repo: marmot-protocol/mdk PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-01T16:28:05.759Z
Learning: Applies to crates/*/CHANGELOG.md : Every change that modifies functionality must update the appropriate crate CHANGELOG.md file (mdk-core, mdk-storage-traits, mdk-memory-storage, or mdk-sqlite-storage)
Applied to files:
crates/mdk-storage-traits/CHANGELOG.md
📚 Learning: 2026-01-01T16:28:05.759Z
Learnt from: CR
Repo: marmot-protocol/mdk PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-01T16:28:05.759Z
Learning: Applies to crates/*/CHANGELOG.md : Always include a PR reference link in changelog entries using the format `([#123](https://github.com/marmot-protocol/mdk/pull/123))`
Applied to files:
crates/mdk-storage-traits/CHANGELOG.md
📚 Learning: 2026-01-01T15:51:58.505Z
Learnt from: dannym-arx
Repo: marmot-protocol/mdk PR: 98
File: crates/mdk-core/CHANGELOG.md:46-46
Timestamp: 2026-01-01T15:51:58.505Z
Learning: In CHANGELOG.md files, keep the GitHub issue references (e.g., #61) as-is; do not convert '#61' (with no space) into an MD heading '# 61'. The hash without a space is intentional for GitHub auto-linking. This guideline applies to all CHANGELOG.md files in the repo.
Applied to files:
crates/mdk-storage-traits/CHANGELOG.md
⏰ 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). (1)
- GitHub Check: Test Coverage
🔇 Additional comments (2)
crates/mdk-sqlite-storage/src/groups.rs (2)
150-157: LGTM! Backward-compatible delegation.The delegation to
messages_paginatedwith the default limit maintains backward compatibility while adding the safety of bounded queries.
472-579: Excellent test coverage for pagination.The test comprehensively covers:
- Multiple pages with proper offset handling
- Ordering (newest first)
- Edge cases (zero limit, exceeding MAX, non-existent group, offset beyond data)
- No overlap between pages
- Backward compatibility via default
messages()method
✅ Coverage: 86.35% → 86.58% (+0.23%) |
jgmontoya
left a comment
There was a problem hiding this comment.
Similarly to #110 (review): the get_messages method in crates/mdk-core/src/messages.rs used to return all messages, and now returns just a single page (undocumented breaking change).
As before (although here I think it's way more relevant/important), it's weird that there's no way for a user to access the different pages.
|
my comments here are also basically the same as on my review of #110 |
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (2)
crates/mdk-storage-traits/src/groups/mod.rs (2)
28-35: MissingPartialEqandEqderives per coding guidelines.The
Paginationstruct is a public type and should derivePartialEqandEqfor comparison operations. As per coding guidelines, derive order should be:Debug,Clone,Copy,PartialEq,Eq,Hash.🔎 Proposed fix
/// Pagination parameters for querying messages -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Pagination { /// Maximum number of messages to return pub limit: Option<usize>, /// Number of messages to skip pub offset: Option<usize>, }Based on coding guidelines: "Derive
Debug,Clone,Copy,PartialEq,Eq, andHashfor public types when possible, in that order"
54-61: Consider simplifying by derivingDefault.The accessor methods (
limit()andoffset()) already provide fallback values viaunwrap_or. DerivingDefaultwould yieldPagination { limit: None, offset: None }, which produces identical behavior through the accessors. This would reduce boilerplate.🔎 Proposed simplification
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] pub struct Pagination { ... } - -impl Default for Pagination { - fn default() -> Self { - Self { - limit: Some(DEFAULT_MESSAGE_LIMIT), - offset: Some(0), - } - } -}If explicit values in
Defaultare preferred for documentation/debugging purposes, the current approach is also fine.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (10)
crates/mdk-core/CHANGELOG.mdcrates/mdk-core/src/messages.rscrates/mdk-memory-storage/CHANGELOG.mdcrates/mdk-memory-storage/src/groups.rscrates/mdk-sqlite-storage/CHANGELOG.mdcrates/mdk-sqlite-storage/src/groups.rscrates/mdk-storage-traits/CHANGELOG.mdcrates/mdk-storage-traits/src/groups/mod.rscrates/mdk-storage-traits/src/test_utils.rscrates/mdk-storage-traits/tests/shared/group_tests.rs
🧰 Additional context used
📓 Path-based instructions (3)
**/*.rs
📄 CodeRabbit inference engine (.cursor/rules/rust.mdc)
**/*.rs: All trait bounds should be written inwhereclauses rather than inline with type parameters in Rust
UseSelfinstead of the explicit type name when referring to the type in impl blocks
DeriveDebug,Clone,Copy,PartialEq,Eq, andHashfor public types when possible, in that order
DeriveDefaultfor public types when there is a reasonable default value
Always use full paths for logging withtracing::<op>!(...)instead of importing and using<op>!(...)
Prefer.to_string()or.to_owned()for&strtoStringconversion instead of.into()orString::from()
Order imports in the following sequence: (1) core/alloc/std, (2) external crates, (3) current sub-modules with mod declarations, (4) internal crate modules
Import thefmtmodule rather than using full paths when implementing traits fromcore::fmtorstd::fmt
Use relativeself::imports for sub-modules rather than bare module names
Place allusestatements at the top of the file following the specified import order, never inside functions, methods, or other blocks
Avoidif let ... { } else { }constructs; usematchinstead when both branches are non-empty
Useif let ... { }without an else clause when a match arm would be intentionally empty
Define sub-modules using separate files (e.g.,x.rs) withmod x;declaration rather than inlinemod x { .. }blocks, except fortestsandbenchesmodules
Use inlinemod tests { #[cfg(test)] ... }for test modules rather than creating separatetests.rsfiles
Use inlinemod benches { #[cfg(bench)] ... }for benchmark modules rather than creating separatebenches.rsfiles
**/*.rs: All trait bounds in generics must be placed inwhereclauses, not inline
UseSelfinstead of the type name when possible
Derive order must be:Debug,Clone,Copy,PartialEq,Eq,Hash(in this order)
Always usetracing::warn!(...), never import and usewarn!(...)
Use.to_string()or.to_owned()for string ...
Files:
crates/mdk-storage-traits/tests/shared/group_tests.rscrates/mdk-storage-traits/src/test_utils.rscrates/mdk-core/src/messages.rscrates/mdk-sqlite-storage/src/groups.rscrates/mdk-memory-storage/src/groups.rscrates/mdk-storage-traits/src/groups/mod.rs
**/*.md
📄 CodeRabbit inference engine (AGENTS.md)
Never use bare URLs in markdown files; all URLs must be properly formatted using
[link text](url),<url>, or([#123](url))for PR/issue references
Files:
crates/mdk-storage-traits/CHANGELOG.mdcrates/mdk-sqlite-storage/CHANGELOG.mdcrates/mdk-core/CHANGELOG.mdcrates/mdk-memory-storage/CHANGELOG.md
crates/*/CHANGELOG.md
📄 CodeRabbit inference engine (AGENTS.md)
Every crate CHANGELOG entry must include a PR reference using the format
([#123](https://github.com/marmot-protocol/mdk/pull/123))
Files:
crates/mdk-storage-traits/CHANGELOG.mdcrates/mdk-sqlite-storage/CHANGELOG.mdcrates/mdk-core/CHANGELOG.mdcrates/mdk-memory-storage/CHANGELOG.md
🧠 Learnings (5)
📚 Learning: 2026-01-03T08:39:39.771Z
Learnt from: CR
Repo: marmot-protocol/mdk PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-03T08:39:39.771Z
Learning: Every change that modifies functionality must update the CHANGELOG in the affected crate
Applied to files:
crates/mdk-storage-traits/CHANGELOG.mdcrates/mdk-sqlite-storage/CHANGELOG.mdcrates/mdk-memory-storage/CHANGELOG.md
📚 Learning: 2026-01-03T08:39:39.771Z
Learnt from: CR
Repo: marmot-protocol/mdk PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-03T08:39:39.771Z
Learning: Applies to crates/*/CHANGELOG.md : Every crate CHANGELOG entry must include a PR reference using the format `([#123](https://github.com/marmot-protocol/mdk/pull/123))`
Applied to files:
crates/mdk-storage-traits/CHANGELOG.mdcrates/mdk-sqlite-storage/CHANGELOG.mdcrates/mdk-memory-storage/CHANGELOG.md
📚 Learning: 2026-01-01T15:51:58.505Z
Learnt from: dannym-arx
Repo: marmot-protocol/mdk PR: 98
File: crates/mdk-core/CHANGELOG.md:46-46
Timestamp: 2026-01-01T15:51:58.505Z
Learning: In CHANGELOG.md files, keep the GitHub issue references (e.g., #61) as-is; do not convert '#61' (with no space) into an MD heading '# 61'. The hash without a space is intentional for GitHub auto-linking. This guideline applies to all CHANGELOG.md files in the repo.
Applied to files:
crates/mdk-storage-traits/CHANGELOG.mdcrates/mdk-sqlite-storage/CHANGELOG.mdcrates/mdk-core/CHANGELOG.mdcrates/mdk-memory-storage/CHANGELOG.md
📚 Learning: 2025-12-03T10:18:10.432Z
Learnt from: CR
Repo: marmot-protocol/mdk PR: 0
File: .cursor/rules/rust.mdc:0-0
Timestamp: 2025-12-03T10:18:10.432Z
Learning: Applies to **/*.rs : Place all `use` statements at the top of the file following the specified import order, never inside functions, methods, or other blocks
Applied to files:
crates/mdk-sqlite-storage/src/groups.rs
📚 Learning: 2026-01-03T08:39:39.771Z
Learnt from: CR
Repo: marmot-protocol/mdk PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-03T08:39:39.771Z
Learning: Applies to **/*.rs : Place all `use` statements at file top, never inside functions
Applied to files:
crates/mdk-sqlite-storage/src/groups.rs
🧬 Code graph analysis (1)
crates/mdk-storage-traits/src/groups/mod.rs (2)
crates/mdk-memory-storage/src/groups.rs (1)
messages(54-94)crates/mdk-sqlite-storage/src/groups.rs (1)
messages(157-205)
🔇 Additional comments (14)
crates/mdk-sqlite-storage/CHANGELOG.md (1)
30-53: LGTM!All CHANGELOG entries properly format PR references and follow the required
([#111](url))pattern. Entries are well-organized across appropriate sections (Breaking changes, Changed, Added, Fixed, Removed).crates/mdk-core/CHANGELOG.md (1)
36-44: LGTM!PR references are properly formatted as
([#111](url)). Entries are placed in correct sections (Added and Fixed) and align with the pagination feature implementation.crates/mdk-memory-storage/CHANGELOG.md (1)
30-46: LGTM!All PR references properly formatted as
([#111](url)). Entries are consistently organized across sections matching the SQLite storage crate CHANGELOG, maintaining clarity about breaking changes, pagination support, and security fixes.crates/mdk-storage-traits/src/test_utils.rs (1)
468-468: LGTM! Pagination parameter correctly added.The call site has been properly updated to pass
Nonefor the pagination parameter, which aligns with the newmessagestrait signature and will use default pagination behavior.crates/mdk-storage-traits/tests/shared/group_tests.rs (1)
351-351: LGTM! Test updated for new pagination API.The test correctly exercises the updated
messagesmethod withNonepagination, verifying default pagination behavior works as expected.crates/mdk-storage-traits/CHANGELOG.md (1)
30-47: LGTM! CHANGELOG properly documented with PR references.All entries correctly include PR references in the required format
([#111](https://github.com/marmot-protocol/mdk/pull/111)). The documentation clearly explains the breaking changes, security fix for memory exhaustion, and the removal of the offset cap.crates/mdk-core/src/messages.rs (2)
15-15: LGTM! Import correctly placed.The
Paginationimport is properly positioned at the file top with other imports from the storage traits module.
102-137: LGTM! Well-designed pagination API with backward compatibility.The refactoring maintains backward compatibility by having
get_messagesdelegate toget_messages_paginatedwithNone. The new method is well-documented with clear examples showing how to use pagination. The implementation correctly delegates to the storage layer with the pagination parameter.crates/mdk-sqlite-storage/src/groups.rs (3)
13-18: LGTM! Test imports correctly positioned with conditional compilation.The test-only imports are properly placed at the file top with
#[cfg(test)]attributes, following Rust conventions for conditional compilation. This addresses the previous review concern about imports inside functions.Based on learnings, imports are correctly at file top.
157-205: LGTM! Solid pagination implementation with proper validation.The implementation correctly:
- Unwraps pagination with default behavior when
None- Validates limit bounds (1 to MAX_MESSAGE_LIMIT)
- Verifies group existence before querying
- Uses parameterized SQL queries preventing injection
- Orders results by
created_at DESCfor newest-first retrievalThe removal of offset validation is intentional per the CHANGELOG to support legitimate large-scale use cases.
467-591: LGTM! Excellent comprehensive test coverage.The test thoroughly validates:
- Basic pagination with multiple pages
- Default behavior when pagination is
None- Non-overlapping pages
- Empty results beyond available messages
- Error handling for invalid limits (0 and exceeding MAX)
- Error for non-existent groups
- Large offset handling (confirming no MAX_OFFSET restriction)
The comprehensive nature of this test is a significant strength, ensuring the pagination implementation is robust and correct.
crates/mdk-memory-storage/src/groups.rs (2)
54-93: LGTM! Pagination implementation is correct and well-structured.The pagination logic properly handles edge cases:
- Validation ensures limit is within bounds before processing
startandendindices are safely clamped tomessages.len()- Group existence check prevents unnecessary work on invalid input
- Sorting before pagination ensures consistent ordering
182-348: Comprehensive test coverage for pagination behavior.The tests effectively validate:
- Default pagination, explicit limits/offsets, boundary conditions
- Error cases (zero limit, exceeding max, non-existent group)
- Ordering guarantees and page non-overlap
- Empty groups and large offsets
crates/mdk-storage-traits/src/groups/mod.rs (1)
80-114: Well-documented API with clear error semantics.The trait method documentation is thorough—covers ordering guarantees, pagination defaults, error conditions, and includes usage examples. The signature is consistent with both memory and SQLite implementations.
@jgmontoya Thank you for catching this! I've documented the change in all relevant CHANGELOGs: |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
crates/mdk-storage-traits/CHANGELOG.md (1)
30-31: Changelog entries and PR references look consistent with guidelinesAll new bullets correctly include the
[#111](...)PR reference and avoid bare URLs; duplication of theMAX_MESSAGE_OFFSETremoval under both “Breaking changes” and “Removed” is mildly redundant but acceptable as-is.Also applies to: 37-38, 42-42, 46-46
crates/mdk-uniffi/src/lib.rs (1)
251-260: UniFFI get_messages correctly forwards None but consider exposing paginationForwarding
Noneintomdk.get_messages(&group_id, None)matches the updated core API and keeps default behaviour. However, UniFFI callers now silently get only the default page, with no way to request additional pages or know about the cap. Consider:
- Updating the doc comment to note that only the first page (default limit) is returned.
- Adding a separate UniFFI-exported method that accepts explicit pagination so FFI clients can traverse full history.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
crates/mdk-core/CHANGELOG.mdcrates/mdk-core/examples/mls_memory.rscrates/mdk-core/examples/mls_sqlite.rscrates/mdk-core/src/messages.rscrates/mdk-storage-traits/CHANGELOG.mdcrates/mdk-uniffi/src/lib.rs
🚧 Files skipped from review as they are similar to previous changes (1)
- crates/mdk-core/CHANGELOG.md
🧰 Additional context used
📓 Path-based instructions (3)
**/*.rs
📄 CodeRabbit inference engine (.cursor/rules/rust.mdc)
**/*.rs: All trait bounds should be written inwhereclauses rather than inline with type parameters in Rust
UseSelfinstead of the explicit type name when referring to the type in impl blocks
DeriveDebug,Clone,Copy,PartialEq,Eq, andHashfor public types when possible, in that order
DeriveDefaultfor public types when there is a reasonable default value
Always use full paths for logging withtracing::<op>!(...)instead of importing and using<op>!(...)
Prefer.to_string()or.to_owned()for&strtoStringconversion instead of.into()orString::from()
Order imports in the following sequence: (1) core/alloc/std, (2) external crates, (3) current sub-modules with mod declarations, (4) internal crate modules
Import thefmtmodule rather than using full paths when implementing traits fromcore::fmtorstd::fmt
Use relativeself::imports for sub-modules rather than bare module names
Place allusestatements at the top of the file following the specified import order, never inside functions, methods, or other blocks
Avoidif let ... { } else { }constructs; usematchinstead when both branches are non-empty
Useif let ... { }without an else clause when a match arm would be intentionally empty
Define sub-modules using separate files (e.g.,x.rs) withmod x;declaration rather than inlinemod x { .. }blocks, except fortestsandbenchesmodules
Use inlinemod tests { #[cfg(test)] ... }for test modules rather than creating separatetests.rsfiles
Use inlinemod benches { #[cfg(bench)] ... }for benchmark modules rather than creating separatebenches.rsfiles
**/*.rs: All trait bounds in generics must be placed inwhereclauses, not inline
UseSelfinstead of the type name when possible
Derive order must be:Debug,Clone,Copy,PartialEq,Eq,Hash(in this order)
Always usetracing::warn!(...), never import and usewarn!(...)
Use.to_string()or.to_owned()for string ...
Files:
crates/mdk-core/examples/mls_memory.rscrates/mdk-core/src/messages.rscrates/mdk-core/examples/mls_sqlite.rscrates/mdk-uniffi/src/lib.rs
**/*.md
📄 CodeRabbit inference engine (AGENTS.md)
Never use bare URLs in markdown files; all URLs must be properly formatted using
[link text](url),<url>, or([#123](url))for PR/issue references
Files:
crates/mdk-storage-traits/CHANGELOG.md
crates/*/CHANGELOG.md
📄 CodeRabbit inference engine (AGENTS.md)
Every crate CHANGELOG entry must include a PR reference using the format
([#123](https://github.com/marmot-protocol/mdk/pull/123))
Files:
crates/mdk-storage-traits/CHANGELOG.md
🧠 Learnings (4)
📚 Learning: 2025-11-30T11:28:06.561Z
Learnt from: dannym-arx
Repo: marmot-protocol/mdk PR: 19
File: crates/mdk-uniffi/src/README.md:16-23
Timestamp: 2025-11-30T11:28:06.561Z
Learning: In the mdk-uniffi crate (Rust UniFFI bindings for MDK), multi-threading support is intentionally not provided. The Mdk instance should be used in a single-threaded context only.
Applied to files:
crates/mdk-uniffi/src/lib.rs
📚 Learning: 2026-01-03T08:39:39.771Z
Learnt from: CR
Repo: marmot-protocol/mdk PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-03T08:39:39.771Z
Learning: Every change that modifies functionality must update the CHANGELOG in the affected crate
Applied to files:
crates/mdk-storage-traits/CHANGELOG.md
📚 Learning: 2026-01-03T08:39:39.771Z
Learnt from: CR
Repo: marmot-protocol/mdk PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-03T08:39:39.771Z
Learning: Applies to crates/*/CHANGELOG.md : Every crate CHANGELOG entry must include a PR reference using the format `([#123](https://github.com/marmot-protocol/mdk/pull/123))`
Applied to files:
crates/mdk-storage-traits/CHANGELOG.md
📚 Learning: 2026-01-01T15:51:58.505Z
Learnt from: dannym-arx
Repo: marmot-protocol/mdk PR: 98
File: crates/mdk-core/CHANGELOG.md:46-46
Timestamp: 2026-01-01T15:51:58.505Z
Learning: In CHANGELOG.md files, keep the GitHub issue references (e.g., #61) as-is; do not convert '#61' (with no space) into an MD heading '# 61'. The hash without a space is intentional for GitHub auto-linking. This guideline applies to all CHANGELOG.md files in the repo.
Applied to files:
crates/mdk-storage-traits/CHANGELOG.md
🧬 Code graph analysis (3)
crates/mdk-core/examples/mls_memory.rs (5)
crates/mdk-core/src/messages.rs (1)
get_messages(117-125)crates/mdk-uniffi/src/lib.rs (1)
get_messages(252-260)crates/mdk-storage-traits/src/groups/mod.rs (1)
messages(110-114)crates/mdk-memory-storage/src/groups.rs (1)
messages(54-94)crates/mdk-sqlite-storage/src/groups.rs (1)
messages(157-205)
crates/mdk-core/src/messages.rs (4)
crates/mdk-uniffi/src/lib.rs (2)
get_messages(252-260)result(371-379)crates/mdk-storage-traits/src/groups/mod.rs (1)
messages(110-114)crates/mdk-memory-storage/src/groups.rs (1)
messages(54-94)crates/mdk-sqlite-storage/src/groups.rs (1)
messages(157-205)
crates/mdk-core/examples/mls_sqlite.rs (5)
crates/mdk-core/src/messages.rs (1)
get_messages(117-125)crates/mdk-uniffi/src/lib.rs (1)
get_messages(252-260)crates/mdk-storage-traits/src/groups/mod.rs (1)
messages(110-114)crates/mdk-memory-storage/src/groups.rs (1)
messages(54-94)crates/mdk-sqlite-storage/src/groups.rs (1)
messages(157-205)
🔇 Additional comments (6)
crates/mdk-core/examples/mls_memory.rs (1)
179-181: Example updates to get_messages align with new paginated APIAll example call sites now pass the extra
Noneparameter toget_messages, matching the new(&GroupId, Option<Pagination>)signature while preserving the intended behaviour in this small-message example.Also applies to: 207-210, 222-224, 231-233
crates/mdk-core/examples/mls_sqlite.rs (1)
188-190: SQLite example get_messages calls correctly updated for paginationEach example now passes
Noneas the pagination argument, matching the updated API while leaving the example semantics unchanged for these small message sets.Also applies to: 216-219, 231-235, 243-245
crates/mdk-core/src/messages.rs (4)
1091-1101: Tests now correctly exercise the default (None) pagination pathThe updated test callsites:
test_get_messages_empty_grouptest_get_messages_for_grouptest_create_message_ensures_rumor_idnow use
mdk.get_messages(&group_id, None), which matches the new API and validates that the default pagination (limit/offset fromPagination::default()) still behaves as the previous “get all messages” semantics for small groups. No changes needed here.Also applies to: 1263-1295, 1395-1408
1706-1716: Non-existent group and multi-client tests correctly updated to use paginated get_messages
test_get_messages_nonexistent_groupnow callsget_messages(&non_existent_group_id, None)and asserts an error, which is consistent with storage backends returningInvalidParametersfor unknown groups.test_multi_client_message_synchronizationusesget_messages(&group_id, None)for both Alice and Bob to verify synchronized histories; this remains valid since message counts are well below the default limit.These updates correctly track the new API while preserving the intended assertions.
Also applies to: 2459-2479
2869-2880: Idempotency and ordering tests correctly adapted to paginated retrievalIn:
test_duplicate_message_from_multiple_relaystest_single_client_message_idempotencytest_message_processing_order_independenceall
get_messagesusages now passNonefor pagination, and the tests still assert on small, bounded message counts. This ensures the idempotency and ordering guarantees are checked against the default page without relying on unbounded retrieval.Also applies to: 2896-2937, 2953-2986
3189-3200: Epoch/extended-offline tests use the new API correctlyThe updated
get_messages(&group_id, None)calls in:
test_extended_offline_period_synctest_message_processing_across_epochscorrectly align with the paginated API while continuing to assert on modest histories (≤ a few messages). These tests now explicitly validate that default pagination still returns the full logical history in these small-scale scenarios.
Also applies to: 3505-3524
✅ Coverage: 86.6% → 86.85% (+0.25%) |
There was a problem hiding this comment.
Actionable comments posted: 1
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
crates/mdk-core/src/messages.rs
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs
📄 CodeRabbit inference engine (.cursor/rules/rust.mdc)
**/*.rs: All trait bounds should be written inwhereclauses rather than inline with type parameters in Rust
UseSelfinstead of the explicit type name when referring to the type in impl blocks
DeriveDebug,Clone,Copy,PartialEq,Eq, andHashfor public types when possible, in that order
DeriveDefaultfor public types when there is a reasonable default value
Always use full paths for logging withtracing::<op>!(...)instead of importing and using<op>!(...)
Prefer.to_string()or.to_owned()for&strtoStringconversion instead of.into()orString::from()
Order imports in the following sequence: (1) core/alloc/std, (2) external crates, (3) current sub-modules with mod declarations, (4) internal crate modules
Import thefmtmodule rather than using full paths when implementing traits fromcore::fmtorstd::fmt
Use relativeself::imports for sub-modules rather than bare module names
Place allusestatements at the top of the file following the specified import order, never inside functions, methods, or other blocks
Avoidif let ... { } else { }constructs; usematchinstead when both branches are non-empty
Useif let ... { }without an else clause when a match arm would be intentionally empty
Define sub-modules using separate files (e.g.,x.rs) withmod x;declaration rather than inlinemod x { .. }blocks, except fortestsandbenchesmodules
Use inlinemod tests { #[cfg(test)] ... }for test modules rather than creating separatetests.rsfiles
Use inlinemod benches { #[cfg(bench)] ... }for benchmark modules rather than creating separatebenches.rsfiles
**/*.rs: All trait bounds in generics must be placed inwhereclauses, not inline
UseSelfinstead of the type name when possible
Derive order must be:Debug,Clone,Copy,PartialEq,Eq,Hash(in this order)
Always usetracing::warn!(...), never import and usewarn!(...)
Use.to_string()or.to_owned()for string ...
Files:
crates/mdk-core/src/messages.rs
⏰ 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). (1)
- GitHub Check: Test Coverage
🔇 Additional comments (2)
crates/mdk-core/src/messages.rs (2)
117-125: API change correctly implemented.The function signature change to accept
Option<Pagination>is correctly implemented and properly delegates to the storage layer. The breaking change is documented in the CHANGELOG per the PR objectives, and passingNonemaintains the default behavior (retrieving the default page size).
88-116: Documentation examples are correct and properly address the previous concern.The doc examples have been verified against the actual
Pagination::newsignature frommdk_storage_traits::groups. The calls toPagination::new(Some(100), Some(0))correctly match the constructor which accepts(limit: Option<usize>, offset: Option<usize>). The examples accurately demonstrate both default pagination (passingNone) and custom pagination (passingSome(Pagination::new(...))), resolving the previous review comment's concern about mismatched signatures.
✅ Coverage: 86.6% → 86.87% (+0.27%) |
There was a problem hiding this comment.
Actionable comments posted: 1
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
crates/mdk-core/src/messages.rs
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs
📄 CodeRabbit inference engine (.cursor/rules/rust.mdc)
**/*.rs: All trait bounds should be written inwhereclauses rather than inline with type parameters in Rust
UseSelfinstead of the explicit type name when referring to the type in impl blocks
DeriveDebug,Clone,Copy,PartialEq,Eq, andHashfor public types when possible, in that order
DeriveDefaultfor public types when there is a reasonable default value
Always use full paths for logging withtracing::<op>!(...)instead of importing and using<op>!(...)
Prefer.to_string()or.to_owned()for&strtoStringconversion instead of.into()orString::from()
Order imports in the following sequence: (1) core/alloc/std, (2) external crates, (3) current sub-modules with mod declarations, (4) internal crate modules
Import thefmtmodule rather than using full paths when implementing traits fromcore::fmtorstd::fmt
Use relativeself::imports for sub-modules rather than bare module names
Place allusestatements at the top of the file following the specified import order, never inside functions, methods, or other blocks
Avoidif let ... { } else { }constructs; usematchinstead when both branches are non-empty
Useif let ... { }without an else clause when a match arm would be intentionally empty
Define sub-modules using separate files (e.g.,x.rs) withmod x;declaration rather than inlinemod x { .. }blocks, except fortestsandbenchesmodules
Use inlinemod tests { #[cfg(test)] ... }for test modules rather than creating separatetests.rsfiles
Use inlinemod benches { #[cfg(bench)] ... }for benchmark modules rather than creating separatebenches.rsfiles
**/*.rs: All trait bounds in generics must be placed inwhereclauses, not inline
UseSelfinstead of the type name when possible
Derive order must be:Debug,Clone,Copy,PartialEq,Eq,Hash(in this order)
Always usetracing::warn!(...), never import and usewarn!(...)
Use.to_string()or.to_owned()for string ...
Files:
crates/mdk-core/src/messages.rs
🧠 Learnings (1)
📓 Common learnings
Learnt from: CR
Repo: marmot-protocol/mdk PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-03T08:39:39.771Z
Learning: Applies to crates/*/CHANGELOG.md : Every crate CHANGELOG entry must include a PR reference using the format `([#123](https://github.com/marmot-protocol/mdk/pull/123))`
🧬 Code graph analysis (1)
crates/mdk-core/src/messages.rs (3)
crates/mdk-memory-storage/src/groups.rs (2)
messages(54-91)admins(93-101)crates/mdk-storage-traits/src/groups/mod.rs (3)
messages(110-114)admins(117-117)new(39-41)crates/mdk-sqlite-storage/src/groups.rs (2)
messages(157-202)admins(204-210)
⏰ 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). (1)
- GitHub Check: Test Coverage
🔇 Additional comments (4)
crates/mdk-core/src/messages.rs (4)
15-15: LGTM! Import follows coding guidelines.The
Paginationimport is correctly placed with othermdk_storage_traitsimports and follows the project's import ordering conventions.
88-125: LGTM! Well-documented API with backward compatibility.The updated
get_messagessignature maintains backward compatibility by usingOption<Pagination>, and the documentation provides clear examples for both default and paginated cases. The implementation correctly delegates to the storage layer.
1097-1099: LGTM! Test updates maintain backward compatibility.All existing test calls to
get_messageshave been consistently updated to passNonefor the pagination parameter, maintaining their original behavior while adapting to the new API signature.Also applies to: 1280-1282, 1342-1344, 1464-1466, 2522-2528, 2776-2776, 2937-2939, 2995-2997, 3041-3043, 3259-3259, 3467-3467, 3574-3575
2533-2544: LGTM! Improved test robustness.These changes enhance test stability by acknowledging that messages with equal timestamps may have non-deterministic ordering. Using content-based verification (
contains()) instead of index-based assertions makes the tests more reliable.Also applies to: 3268-3279
✅ Coverage: 86.88% → 87.2% (+0.32%) |
105b944 to
3f2c718
Compare
✅ Coverage: 86.88% → 87.42% (+0.54%) |
a9548ec to
d648c69
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
crates/mdk-memory-storage/CHANGELOG.md (1)
30-55: Fix duplicate section headers and reposition the breaking change forpending_welcomes().The changelog has structural issues:
- Duplicate section headers:
### Changed,### Added, and### Fixedappear twice (lines 32, 45; lines 36, 49; lines 40, 53). Each section should appear only once.- Misplaced breaking change: Line 43 contains a
**BREAKING**:entry forpending_welcomes()(PR #110), but it's positioned under the### Fixedsection instead of the### Breaking changessection.The changelog should have a single set of section headers, with all entries for PRs #111 and #110 grouped under their respective sections.
🔎 Proposed fix: Consolidate sections and reposition breaking changes
## Unreleased ### Breaking changes - **BREAKING**: Updated `messages()` implementation to accept `Option<Pagination>` parameter ([#111](https://github.com/marmot-protocol/mdk/pull/111)) - **BREAKING**: Updated `pending_welcomes()` implementation to accept `Option<Pagination>` parameter ([#110](https://github.com/marmot-protocol/mdk/pull/110)) ### Changed - Simplified validation logic to use range contains pattern for better readability ([#111](https://github.com/marmot-protocol/mdk/pull/111)) - Simplified validation logic to use range contains pattern for better readability ([#110](https://github.com/marmot-protocol/mdk/pull/110)) ### Added - Implemented pagination support using `Pagination` struct for group messages ([#111](https://github.com/marmot-protocol/mdk/pull/111)) - Implemented pagination support using `Pagination` struct for pending welcomes ([#110](https://github.com/marmot-protocol/mdk/pull/110)) ### Fixed - **Security (Audit Issue Z)**: Added pagination to prevent memory exhaustion from unbounded loading of group messages ([#111](https://github.com/marmot-protocol/mdk/pull/111)) - **Security (Audit Issue AA)**: Added pagination to prevent memory exhaustion from unbounded loading of pending welcomes ([#110](https://github.com/marmot-protocol/mdk/pull/110)) - **Security (Audit Issue AO)**: Removed MLS group identifiers from error messages to prevent metadata leakage in logs and telemetry. Error messages now use generic "Group not found" instead of including the sensitive 32-byte MLS group ID. ([#112](https://github.com/marmot-protocol/mdk/pull/112)) - Fix `admins()` to return `InvalidParameters` error when group not found, instead of incorrectly returning `NoAdmins` ([#104](https://github.com/marmot-protocol/mdk/pull/104))crates/mdk-sqlite-storage/CHANGELOG.md (1)
28-62: Fix duplicate section headers and reposition the breaking change forpending_welcomes().The changelog has structural issues similar to mdk-memory-storage:
- Duplicate section headers:
### Changedappears twice (lines 32 and 37).- Misplaced breaking change: Line 35 contains a
**BREAKING**:entry forpending_welcomes()(PR #110), but it's positioned under### Changedinstead of the### Breaking changessection.- Structure clarity: The
### Addedsection (starting line 41) mixes entries from multiple PRs (#94, #111, #110). While not incorrect, this makes it harder to scan.Consolidate to a single set of section headers with all entries properly grouped.
🔎 Proposed fix: Consolidate sections and reposition breaking changes
## Unreleased ### Breaking changes - **BREAKING**: Updated `messages()` implementation to accept `Option<Pagination>` parameter ([#111](https://github.com/marmot-protocol/mdk/pull/111)) - **BREAKING**: Updated `pending_welcomes()` implementation to accept `Option<Pagination>` parameter ([#110](https://github.com/marmot-protocol/mdk/pull/110)) ### Changed - Simplified validation logic to use range contains pattern for better readability ([#111](https://github.com/marmot-protocol/mdk/pull/111)) - Simplified validation logic to use range contains pattern for better readability ([#110](https://github.com/marmot-protocol/mdk/pull/110)) ### Added - Input validation for storage operations to prevent unbounded writes ([#94](https://github.com/marmot-protocol/mdk/pull/94)) - Message content limited to 1MB - Group names limited to 255 bytes - Group descriptions limited to 2000 bytes - JSON fields limited to 50-100KB - New `Validation` error variant for validation failures - Implemented pagination support using `Pagination` struct for group messages ([#111](https://github.com/marmot-protocol/mdk/pull/111)) - Implemented pagination support using `Pagination` struct for pending welcomes ([#110](https://github.com/marmot-protocol/mdk/pull/110)) ### Fixed - **Security (Audit Issue Z)**: Added pagination to prevent memory exhaustion from unbounded loading of group messages ([#111](https://github.com/marmot-protocol/mdk/pull/111)) - **Security (Audit Issue AA)**: Added pagination to prevent memory exhaustion from unbounded loading of pending welcomes ([#110](https://github.com/marmot-protocol/mdk/pull/110)) - **Security (Audit Issue AB)**: Added size limits to prevent disk and CPU exhaustion from unbounded user input ([#94](https://github.com/marmot-protocol/mdk/pull/94)) - Propagate `last_message_id` parse errors in `row_to_group` instead of silently converting to `None` ([#105](https://github.com/marmot-protocol/mdk/pull/105)) - **Security (Audit Issue AG)**: `all_groups` now skips corrupted rows instead of failing on the first deserialization error, improving availability when database contains malformed data ([#115](https://github.com/marmot-protocol/mdk/pull/115)) - **Security (Audit Issue AO)**: Removed MLS group identifiers from error messages to prevent metadata leakage in logs and telemetry. Error messages now use generic "Group not found" instead of including the sensitive 32-byte MLS group ID. ([#112](https://github.com/marmot-protocol/mdk/pull/112))
🤖 Fix all issues with AI Agents
In @crates/mdk-storage-traits/CHANGELOG.md:
- Around line 37-40: Remove the duplicate "Added `Pagination` struct with
`limit` and `offset` fields" entry that repeats the PR #111 note (the
near-duplicate line referencing the same `Pagination` struct appears after the
first one); delete that second `Pagination` line so only the single correct
entry for the `Pagination` struct remains and leave the other constant entries
intact, then scan for any other accidental duplicates and remove them if
present.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (10)
crates/mdk-core/CHANGELOG.mdcrates/mdk-core/examples/mls_memory.rscrates/mdk-core/examples/mls_sqlite.rscrates/mdk-memory-storage/CHANGELOG.mdcrates/mdk-sqlite-storage/CHANGELOG.mdcrates/mdk-sqlite-storage/src/groups.rscrates/mdk-storage-traits/CHANGELOG.mdcrates/mdk-storage-traits/src/test_utils.rscrates/mdk-uniffi/CHANGELOG.mdcrates/mdk-uniffi/src/lib.rs
🚧 Files skipped from review as they are similar to previous changes (3)
- crates/mdk-storage-traits/src/test_utils.rs
- crates/mdk-core/examples/mls_memory.rs
- crates/mdk-uniffi/CHANGELOG.md
🧰 Additional context used
📓 Path-based instructions (3)
**/*.md
📄 CodeRabbit inference engine (AGENTS.md)
Never use bare URLs in markdown files; all URLs must be properly formatted using
[link text](url),<url>, or([#123](url))for PR/issue references
Files:
crates/mdk-sqlite-storage/CHANGELOG.mdcrates/mdk-core/CHANGELOG.mdcrates/mdk-memory-storage/CHANGELOG.mdcrates/mdk-storage-traits/CHANGELOG.md
crates/*/CHANGELOG.md
📄 CodeRabbit inference engine (AGENTS.md)
Every crate CHANGELOG entry must include a PR reference using the format
([#123](https://github.com/marmot-protocol/mdk/pull/123))
Files:
crates/mdk-sqlite-storage/CHANGELOG.mdcrates/mdk-core/CHANGELOG.mdcrates/mdk-memory-storage/CHANGELOG.mdcrates/mdk-storage-traits/CHANGELOG.md
**/*.rs
📄 CodeRabbit inference engine (.cursor/rules/rust.mdc)
**/*.rs: All trait bounds should be written inwhereclauses rather than inline with type parameters in Rust
UseSelfinstead of the explicit type name when referring to the type in impl blocks
DeriveDebug,Clone,Copy,PartialEq,Eq, andHashfor public types when possible, in that order
DeriveDefaultfor public types when there is a reasonable default value
Always use full paths for logging withtracing::<op>!(...)instead of importing and using<op>!(...)
Prefer.to_string()or.to_owned()for&strtoStringconversion instead of.into()orString::from()
Order imports in the following sequence: (1) core/alloc/std, (2) external crates, (3) current sub-modules with mod declarations, (4) internal crate modules
Import thefmtmodule rather than using full paths when implementing traits fromcore::fmtorstd::fmt
Use relativeself::imports for sub-modules rather than bare module names
Place allusestatements at the top of the file following the specified import order, never inside functions, methods, or other blocks
Avoidif let ... { } else { }constructs; usematchinstead when both branches are non-empty
Useif let ... { }without an else clause when a match arm would be intentionally empty
Define sub-modules using separate files (e.g.,x.rs) withmod x;declaration rather than inlinemod x { .. }blocks, except fortestsandbenchesmodules
Use inlinemod tests { #[cfg(test)] ... }for test modules rather than creating separatetests.rsfiles
Use inlinemod benches { #[cfg(bench)] ... }for benchmark modules rather than creating separatebenches.rsfiles
**/*.rs: All trait bounds in generics must be placed inwhereclauses, not inline
UseSelfinstead of the type name when possible
Derive order must be:Debug,Clone,Copy,PartialEq,Eq,Hash(in this order)
Always usetracing::warn!(...), never import and usewarn!(...)
Use.to_string()or.to_owned()for string ...
Files:
crates/mdk-sqlite-storage/src/groups.rscrates/mdk-uniffi/src/lib.rscrates/mdk-core/examples/mls_sqlite.rs
🧠 Learnings (6)
📚 Learning: 2026-01-03T08:39:39.799Z
Learnt from: CR
Repo: marmot-protocol/mdk PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-03T08:39:39.799Z
Learning: Every change that modifies functionality must update the CHANGELOG in the affected crate
Applied to files:
crates/mdk-sqlite-storage/CHANGELOG.mdcrates/mdk-core/CHANGELOG.mdcrates/mdk-memory-storage/CHANGELOG.mdcrates/mdk-storage-traits/CHANGELOG.md
📚 Learning: 2026-01-03T08:39:39.799Z
Learnt from: CR
Repo: marmot-protocol/mdk PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-03T08:39:39.799Z
Learning: Applies to crates/*/CHANGELOG.md : Every crate CHANGELOG entry must include a PR reference using the format `([#123](https://github.com/marmot-protocol/mdk/pull/123))`
Applied to files:
crates/mdk-sqlite-storage/CHANGELOG.mdcrates/mdk-core/CHANGELOG.mdcrates/mdk-memory-storage/CHANGELOG.mdcrates/mdk-storage-traits/CHANGELOG.md
📚 Learning: 2026-01-01T15:51:58.505Z
Learnt from: dannym-arx
Repo: marmot-protocol/mdk PR: 98
File: crates/mdk-core/CHANGELOG.md:46-46
Timestamp: 2026-01-01T15:51:58.505Z
Learning: In CHANGELOG.md files, keep the GitHub issue references (e.g., #61) as-is; do not convert '#61' (with no space) into an MD heading '# 61'. The hash without a space is intentional for GitHub auto-linking. This guideline applies to all CHANGELOG.md files in the repo.
Applied to files:
crates/mdk-sqlite-storage/CHANGELOG.mdcrates/mdk-core/CHANGELOG.mdcrates/mdk-memory-storage/CHANGELOG.mdcrates/mdk-storage-traits/CHANGELOG.md
📚 Learning: 2025-12-03T10:18:10.432Z
Learnt from: CR
Repo: marmot-protocol/mdk PR: 0
File: .cursor/rules/rust.mdc:0-0
Timestamp: 2025-12-03T10:18:10.432Z
Learning: Applies to **/*.rs : Place all `use` statements at the top of the file following the specified import order, never inside functions, methods, or other blocks
Applied to files:
crates/mdk-sqlite-storage/src/groups.rs
📚 Learning: 2026-01-03T08:39:39.799Z
Learnt from: CR
Repo: marmot-protocol/mdk PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-03T08:39:39.799Z
Learning: Applies to **/*.rs : Place all `use` statements at file top, never inside functions
Applied to files:
crates/mdk-sqlite-storage/src/groups.rs
📚 Learning: 2025-12-12T14:58:57.710Z
Learnt from: CR
Repo: marmot-protocol/mdk PR: 0
File: .cursor/rules/mls.mdc:0-0
Timestamp: 2025-12-12T14:58:57.710Z
Learning: This repository implements the Marmot protocol, which integrates MLS with the Nostr protocol. Reference https://github.com/marmot-protocol/marmot for protocol specifications and design decisions.
Applied to files:
crates/mdk-core/CHANGELOG.md
🧬 Code graph analysis (2)
crates/mdk-uniffi/src/lib.rs (2)
crates/mdk-core/src/messages.rs (2)
get_messages(117-125)test_get_messages_with_pagination(1107-1163)crates/mdk-storage-traits/src/welcomes/mod.rs (3)
limit(40-42)offset(45-47)new(35-37)
crates/mdk-core/examples/mls_sqlite.rs (5)
crates/mdk-uniffi/src/lib.rs (1)
get_messages(264-284)crates/mdk-core/src/messages.rs (1)
get_messages(117-125)crates/mdk-sqlite-storage/src/groups.rs (1)
messages(165-210)crates/mdk-memory-storage/src/groups.rs (1)
messages(54-91)crates/mdk-storage-traits/src/groups/mod.rs (1)
messages(110-114)
🪛 markdownlint-cli2 (0.18.1)
crates/mdk-sqlite-storage/CHANGELOG.md
49-49: Multiple headings with the same content
(MD024, no-duplicate-heading)
53-53: Multiple headings with the same content
(MD024, no-duplicate-heading)
⏰ 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). (1)
- GitHub Check: Test Coverage
🔇 Additional comments (8)
crates/mdk-core/CHANGELOG.md (1)
26-79: LGTM!The changelog is well-structured with proper section organization, all PR references correctly formatted, and clear descriptions of breaking changes and security improvements.
crates/mdk-core/examples/mls_sqlite.rs (1)
188-245: LGTM!The example correctly adapts to the new paginated
get_messagesAPI by passingNoneto use default pagination behavior. This is appropriate for demonstration code where retrieving all messages (up to the default limit) is the expected behavior.crates/mdk-uniffi/src/lib.rs (3)
26-28: LGTM!Good use of import aliases to distinguish between
MessagePaginationandWelcomePaginationtypes, improving code clarity.
253-284: Well-documented pagination API extension.The implementation correctly:
- Creates pagination only when at least one parameter is specified
- Allows partial specification (e.g., only limit or only offset) while using defaults for the other
- Provides clear documentation with default values
1210-1295: Comprehensive pagination test coverage.The test validates key pagination scenarios including default behavior, explicit parameters, boundary conditions (offset beyond available), and small page sizes. Good coverage for the UniFFI layer.
crates/mdk-sqlite-storage/src/groups.rs (3)
8-18: LGTM!Import organization follows coding guidelines: standard library first, external crates next, test-only imports with
#[cfg(test)], and internal crate imports last.
165-210: Solid pagination implementation.The implementation:
- Applies default pagination when
Noneis passed- Validates limit bounds with clear error messages
- Pushes pagination to the database layer for efficiency (LIMIT/OFFSET in SQL)
- Correctly returns newest messages first via
ORDER BY created_at DESC
459-583: Excellent test coverage for pagination.The test thoroughly validates:
- Multi-page retrieval with correct boundaries
- No overlap between pages (important for pagination correctness)
- Correct ordering (newest first)
- Edge cases: limit=0 error, limit>MAX error, non-existent group error
- Large offset handling (returns empty, no error)
✅ Coverage: 86.88% → 87.42% (+0.54%) |
d648c69 to
90eba3d
Compare
✅ Coverage: 86.88% → 87.42% (+0.54%) |
There was a problem hiding this comment.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
crates/mdk-memory-storage/CHANGELOG.md (1)
26-56: Consolidate duplicate section headers in the Unreleased section.The Unreleased section contains duplicate headers (### Changed, ### Added, ### Fixed) because entries from multiple PRs (#110 and #111) were added separately. This violates markdown best practices and triggers the markdownlint warning.
Please consolidate entries from both PRs under single section headers:
- Merge both "### Changed" sections
- Merge both "### Added" sections
- Merge both "### Fixed" sections
🔎 Suggested structure
## Unreleased ### Breaking changes - **BREAKING**: Updated `messages()` implementation to accept `Option<Pagination>` parameter ([#111](https://github.com/marmot-protocol/mdk/pull/111)) +- **BREAKING**: Updated `pending_welcomes()` implementation to accept `Option<Pagination>` parameter ([#110](https://github.com/marmot-protocol/mdk/pull/110)) ### Changed - Simplified validation logic to use range contains pattern for better readability ([#111](https://github.com/marmot-protocol/mdk/pull/111)) +- Simplified validation logic to use range contains pattern for better readability ([#110](https://github.com/marmot-protocol/mdk/pull/110)) ### Added - Implemented pagination support using `Pagination` struct for group messages ([#111](https://github.com/marmot-protocol/mdk/pull/111)) +- Implemented pagination support using `Pagination` struct for pending welcomes ([#110](https://github.com/marmot-protocol/mdk/pull/110)) ### Fixed - **Security (Audit Issue Z)**: Added pagination to prevent memory exhaustion from unbounded loading of group messages ([#111](https://github.com/marmot-protocol/mdk/pull/111)) -- **BREAKING**: Updated `pending_welcomes()` implementation to accept `Option<Pagination>` parameter ([#110](https://github.com/marmot-protocol/mdk/pull/110)) - -### Changed - -- Simplified validation logic to use range contains pattern for better readability ([#110](https://github.com/marmot-protocol/mdk/pull/110)) - -### Added - -- Implemented pagination support using `Pagination` struct for pending welcomes ([#110](https://github.com/marmot-protocol/mdk/pull/110)) - -### Fixed - - **Security (Audit Issue AA)**: Added pagination to prevent memory exhaustion from unbounded loading of pending welcomes ([#110](https://github.com/marmot-protocol/mdk/pull/110)) - **Security (Audit Issue AO)**: Removed MLS group identifiers from error messages to prevent metadata leakage in logs and telemetry. Error messages now use generic "Group not found" instead of including the sensitive 32-byte MLS group ID. ([#112](https://github.com/marmot-protocol/mdk/pull/112)) - Fix `admins()` to return `InvalidParameters` error when group not found, instead of incorrectly returning `NoAdmins` ([#104](https://github.com/marmot-protocol/mdk/pull/104))Based on learnings, every change that modifies functionality must update the CHANGELOG in the affected crate.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (10)
crates/mdk-core/CHANGELOG.mdcrates/mdk-core/examples/mls_memory.rscrates/mdk-core/examples/mls_sqlite.rscrates/mdk-memory-storage/CHANGELOG.mdcrates/mdk-sqlite-storage/CHANGELOG.mdcrates/mdk-sqlite-storage/src/groups.rscrates/mdk-storage-traits/CHANGELOG.mdcrates/mdk-storage-traits/src/test_utils.rscrates/mdk-uniffi/CHANGELOG.mdcrates/mdk-uniffi/src/lib.rs
🚧 Files skipped from review as they are similar to previous changes (5)
- crates/mdk-storage-traits/src/test_utils.rs
- crates/mdk-sqlite-storage/CHANGELOG.md
- crates/mdk-core/examples/mls_sqlite.rs
- crates/mdk-core/examples/mls_memory.rs
- crates/mdk-uniffi/CHANGELOG.md
🧰 Additional context used
📓 Path-based instructions (3)
**/*.rs
📄 CodeRabbit inference engine (.cursor/rules/rust.mdc)
**/*.rs: All trait bounds should be written inwhereclauses rather than inline with type parameters in Rust
UseSelfinstead of the explicit type name when referring to the type in impl blocks
DeriveDebug,Clone,Copy,PartialEq,Eq, andHashfor public types when possible, in that order
DeriveDefaultfor public types when there is a reasonable default value
Always use full paths for logging withtracing::<op>!(...)instead of importing and using<op>!(...)
Prefer.to_string()or.to_owned()for&strtoStringconversion instead of.into()orString::from()
Order imports in the following sequence: (1) core/alloc/std, (2) external crates, (3) current sub-modules with mod declarations, (4) internal crate modules
Import thefmtmodule rather than using full paths when implementing traits fromcore::fmtorstd::fmt
Use relativeself::imports for sub-modules rather than bare module names
Place allusestatements at the top of the file following the specified import order, never inside functions, methods, or other blocks
Avoidif let ... { } else { }constructs; usematchinstead when both branches are non-empty
Useif let ... { }without an else clause when a match arm would be intentionally empty
Define sub-modules using separate files (e.g.,x.rs) withmod x;declaration rather than inlinemod x { .. }blocks, except fortestsandbenchesmodules
Use inlinemod tests { #[cfg(test)] ... }for test modules rather than creating separatetests.rsfiles
Use inlinemod benches { #[cfg(bench)] ... }for benchmark modules rather than creating separatebenches.rsfiles
**/*.rs: All trait bounds in generics must be placed inwhereclauses, not inline
UseSelfinstead of the type name when possible
Derive order must be:Debug,Clone,Copy,PartialEq,Eq,Hash(in this order)
Always usetracing::warn!(...), never import and usewarn!(...)
Use.to_string()or.to_owned()for string ...
Files:
crates/mdk-sqlite-storage/src/groups.rscrates/mdk-uniffi/src/lib.rs
**/*.md
📄 CodeRabbit inference engine (AGENTS.md)
Never use bare URLs in markdown files; all URLs must be properly formatted using
[link text](url),<url>, or([#123](url))for PR/issue references
Files:
crates/mdk-storage-traits/CHANGELOG.mdcrates/mdk-core/CHANGELOG.mdcrates/mdk-memory-storage/CHANGELOG.md
crates/*/CHANGELOG.md
📄 CodeRabbit inference engine (AGENTS.md)
Every crate CHANGELOG entry must include a PR reference using the format
([#123](https://github.com/marmot-protocol/mdk/pull/123))
Files:
crates/mdk-storage-traits/CHANGELOG.mdcrates/mdk-core/CHANGELOG.mdcrates/mdk-memory-storage/CHANGELOG.md
🧠 Learnings (6)
📚 Learning: 2025-12-03T10:18:10.432Z
Learnt from: CR
Repo: marmot-protocol/mdk PR: 0
File: .cursor/rules/rust.mdc:0-0
Timestamp: 2025-12-03T10:18:10.432Z
Learning: Applies to **/*.rs : Place all `use` statements at the top of the file following the specified import order, never inside functions, methods, or other blocks
Applied to files:
crates/mdk-sqlite-storage/src/groups.rs
📚 Learning: 2026-01-03T08:39:39.799Z
Learnt from: CR
Repo: marmot-protocol/mdk PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-03T08:39:39.799Z
Learning: Applies to **/*.rs : Place all `use` statements at file top, never inside functions
Applied to files:
crates/mdk-sqlite-storage/src/groups.rs
📚 Learning: 2026-01-03T08:39:39.799Z
Learnt from: CR
Repo: marmot-protocol/mdk PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-03T08:39:39.799Z
Learning: Every change that modifies functionality must update the CHANGELOG in the affected crate
Applied to files:
crates/mdk-storage-traits/CHANGELOG.mdcrates/mdk-core/CHANGELOG.mdcrates/mdk-memory-storage/CHANGELOG.md
📚 Learning: 2026-01-03T08:39:39.799Z
Learnt from: CR
Repo: marmot-protocol/mdk PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-03T08:39:39.799Z
Learning: Applies to crates/*/CHANGELOG.md : Every crate CHANGELOG entry must include a PR reference using the format `([#123](https://github.com/marmot-protocol/mdk/pull/123))`
Applied to files:
crates/mdk-storage-traits/CHANGELOG.mdcrates/mdk-core/CHANGELOG.mdcrates/mdk-memory-storage/CHANGELOG.md
📚 Learning: 2026-01-01T15:51:58.505Z
Learnt from: dannym-arx
Repo: marmot-protocol/mdk PR: 98
File: crates/mdk-core/CHANGELOG.md:46-46
Timestamp: 2026-01-01T15:51:58.505Z
Learning: In CHANGELOG.md files, keep the GitHub issue references (e.g., #61) as-is; do not convert '#61' (with no space) into an MD heading '# 61'. The hash without a space is intentional for GitHub auto-linking. This guideline applies to all CHANGELOG.md files in the repo.
Applied to files:
crates/mdk-storage-traits/CHANGELOG.mdcrates/mdk-core/CHANGELOG.mdcrates/mdk-memory-storage/CHANGELOG.md
📚 Learning: 2025-12-12T14:58:57.710Z
Learnt from: CR
Repo: marmot-protocol/mdk PR: 0
File: .cursor/rules/mls.mdc:0-0
Timestamp: 2025-12-12T14:58:57.710Z
Learning: This repository implements the Marmot protocol, which integrates MLS with the Nostr protocol. Reference https://github.com/marmot-protocol/marmot for protocol specifications and design decisions.
Applied to files:
crates/mdk-core/CHANGELOG.md
🧬 Code graph analysis (2)
crates/mdk-sqlite-storage/src/groups.rs (3)
crates/mdk-storage-traits/src/groups/mod.rs (4)
messages(110-114)limit(44-46)offset(49-51)new(39-41)crates/mdk-sqlite-storage/src/db.rs (5)
row_to_message(210-255)from(24-26)from(30-32)from(66-68)from(72-74)crates/mdk-sqlite-storage/src/lib.rs (3)
new_in_memory(120-148)from_slice(49-54)new(78-112)
crates/mdk-uniffi/src/lib.rs (2)
crates/mdk-core/src/messages.rs (2)
get_messages(117-125)test_get_messages_with_pagination(1107-1163)crates/mdk-storage-traits/src/welcomes/mod.rs (3)
limit(40-42)offset(45-47)new(35-37)
🪛 markdownlint-cli2 (0.18.1)
crates/mdk-memory-storage/CHANGELOG.md
37-37: Multiple headings with the same content
(MD024, no-duplicate-heading)
⏰ 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). (1)
- GitHub Check: Test Coverage
🔇 Additional comments (6)
crates/mdk-core/CHANGELOG.md (1)
1-79: CHANGELOG entries are well-formatted with proper PR references.All entries in the Unreleased section include PR references in the correct format
([#XXX](https://github.com/...)), and all URLs are properly formatted. The #111-related changes are consistently documented across Breaking changes, Added, and Fixed sections.crates/mdk-uniffi/src/lib.rs (2)
253-284: LGTM: Clean pagination implementation for foreign bindings.The get_messages signature extension and pagination construction logic are well-designed:
- Sensible None-handling: (None, None) → use default pagination
- Safe type conversions: u32 → usize is always valid
- Clear documentation with parameter descriptions
The implementation correctly forwards pagination to the underlying MDK layer and handles the boundary cases appropriately.
1210-1295: LGTM: Comprehensive pagination test coverage.The test validates all critical scenarios:
- Default pagination (None, None)
- Explicit limit and offset
- Offset beyond available messages
- Limit boundary validation
This provides strong evidence that the pagination feature works correctly in the UniFFI layer.
crates/mdk-sqlite-storage/src/groups.rs (3)
13-18: LGTM: Imports properly placed at file top.The test-only imports are correctly placed at the top of the file with
#[cfg(test)]guards, following the coding guideline that allusestatements must be at file top.As per coding guidelines, all imports must be at file top.
165-210: LGTM: Robust pagination implementation with proper validation.The messages() implementation correctly:
- Validates limit is within
1..=MAX_MESSAGE_LIMITrange- Checks group existence before querying (prevents leaking existence info)
- Uses parameterized SQL queries to prevent injection
- Orders by
created_at DESCfor newest-first semantics- Intentionally omits offset validation per test requirements
The error handling and bounds checking align well with the security goals stated in the PR objectives.
459-583: LGTM: Excellent test coverage for pagination behavior.The test comprehensively validates:
- Multi-page retrieval with proper ordering
- No overlap between consecutive pages
- Default pagination behavior (None)
- Boundary conditions (limit 0, exceeding MAX_MESSAGE_LIMIT)
- Error paths (non-existent group, large offset)
This thorough coverage provides high confidence in the pagination implementation's correctness.
✅ Coverage: 86.88% → 87.42% (+0.54%) |
✅ Coverage: 86.88% → 87.42% (+0.54%) |
✅ Coverage: 86.9% → 87.44% (+0.54%) |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In @crates/mdk-uniffi/CHANGELOG.md:
- Line 31: Update the PR reference for the get_pending_welcomes() pagination
note: replace the incorrect "#119" with "#110" where get_pending_welcomes() is
mentioned (the two occurrences in the CHANGELOG entry referencing pagination) so
it matches the documentation in crates/mdk-core/CHANGELOG.md and
crates/mdk-sqlite-storage/CHANGELOG.md.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
crates/mdk-core/CHANGELOG.mdcrates/mdk-memory-storage/CHANGELOG.mdcrates/mdk-sqlite-storage/CHANGELOG.mdcrates/mdk-sqlite-storage/src/groups.rscrates/mdk-storage-traits/CHANGELOG.mdcrates/mdk-uniffi/CHANGELOG.md
🚧 Files skipped from review as they are similar to previous changes (1)
- crates/mdk-memory-storage/CHANGELOG.md
🧰 Additional context used
📓 Path-based instructions (3)
**/*.md
📄 CodeRabbit inference engine (AGENTS.md)
Never use bare URLs in markdown files; all URLs must be properly formatted using
[link text](url),<url>, or([#123](url))for PR/issue references
Files:
crates/mdk-core/CHANGELOG.mdcrates/mdk-storage-traits/CHANGELOG.mdcrates/mdk-uniffi/CHANGELOG.mdcrates/mdk-sqlite-storage/CHANGELOG.md
crates/*/CHANGELOG.md
📄 CodeRabbit inference engine (AGENTS.md)
Every crate CHANGELOG entry must include a PR reference using the format
([#123](https://github.com/marmot-protocol/mdk/pull/123))
Files:
crates/mdk-core/CHANGELOG.mdcrates/mdk-storage-traits/CHANGELOG.mdcrates/mdk-uniffi/CHANGELOG.mdcrates/mdk-sqlite-storage/CHANGELOG.md
**/*.rs
📄 CodeRabbit inference engine (.cursor/rules/rust.mdc)
**/*.rs: All trait bounds should be written inwhereclauses rather than inline with type parameters in Rust
UseSelfinstead of the explicit type name when referring to the type in impl blocks
DeriveDebug,Clone,Copy,PartialEq,Eq, andHashfor public types when possible, in that order
DeriveDefaultfor public types when there is a reasonable default value
Always use full paths for logging withtracing::<op>!(...)instead of importing and using<op>!(...)
Prefer.to_string()or.to_owned()for&strtoStringconversion instead of.into()orString::from()
Order imports in the following sequence: (1) core/alloc/std, (2) external crates, (3) current sub-modules with mod declarations, (4) internal crate modules
Import thefmtmodule rather than using full paths when implementing traits fromcore::fmtorstd::fmt
Use relativeself::imports for sub-modules rather than bare module names
Place allusestatements at the top of the file following the specified import order, never inside functions, methods, or other blocks
Avoidif let ... { } else { }constructs; usematchinstead when both branches are non-empty
Useif let ... { }without an else clause when a match arm would be intentionally empty
Define sub-modules using separate files (e.g.,x.rs) withmod x;declaration rather than inlinemod x { .. }blocks, except fortestsandbenchesmodules
Use inlinemod tests { #[cfg(test)] ... }for test modules rather than creating separatetests.rsfiles
Use inlinemod benches { #[cfg(bench)] ... }for benchmark modules rather than creating separatebenches.rsfiles
**/*.rs: All trait bounds in generics must be placed inwhereclauses, not inline
UseSelfinstead of the type name when possible
Derive order must be:Debug,Clone,Copy,PartialEq,Eq,Hash(in this order)
Always usetracing::warn!(...), never import and usewarn!(...)
Use.to_string()or.to_owned()for string ...
Files:
crates/mdk-sqlite-storage/src/groups.rs
🧠 Learnings (12)
📓 Common learnings
Learnt from: CR
Repo: marmot-protocol/mdk PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-03T08:39:39.799Z
Learning: Applies to crates/*/CHANGELOG.md : Every crate CHANGELOG entry must include a PR reference using the format `([#123](https://github.com/marmot-protocol/mdk/pull/123))`
📚 Learning: 2026-01-03T08:39:39.799Z
Learnt from: CR
Repo: marmot-protocol/mdk PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-03T08:39:39.799Z
Learning: Applies to crates/*/CHANGELOG.md : Every crate CHANGELOG entry must include a PR reference using the format `([#123](https://github.com/marmot-protocol/mdk/pull/123))`
Applied to files:
crates/mdk-core/CHANGELOG.mdcrates/mdk-storage-traits/CHANGELOG.mdcrates/mdk-uniffi/CHANGELOG.mdcrates/mdk-sqlite-storage/CHANGELOG.md
📚 Learning: 2026-01-03T08:39:39.799Z
Learnt from: CR
Repo: marmot-protocol/mdk PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-03T08:39:39.799Z
Learning: Every change that modifies functionality must update the CHANGELOG in the affected crate
Applied to files:
crates/mdk-core/CHANGELOG.mdcrates/mdk-storage-traits/CHANGELOG.mdcrates/mdk-uniffi/CHANGELOG.mdcrates/mdk-sqlite-storage/CHANGELOG.md
📚 Learning: 2025-12-12T14:58:57.710Z
Learnt from: CR
Repo: marmot-protocol/mdk PR: 0
File: .cursor/rules/mls.mdc:0-0
Timestamp: 2025-12-12T14:58:57.710Z
Learning: This repository implements the Marmot protocol, which integrates MLS with the Nostr protocol. Reference https://github.com/marmot-protocol/marmot for protocol specifications and design decisions.
Applied to files:
crates/mdk-core/CHANGELOG.md
📚 Learning: 2026-01-01T15:51:58.505Z
Learnt from: dannym-arx
Repo: marmot-protocol/mdk PR: 98
File: crates/mdk-core/CHANGELOG.md:46-46
Timestamp: 2026-01-01T15:51:58.505Z
Learning: In CHANGELOG.md files, keep the GitHub issue references (e.g., #61) as-is; do not convert '#61' (with no space) into an MD heading '# 61'. The hash without a space is intentional for GitHub auto-linking. This guideline applies to all CHANGELOG.md files in the repo.
Applied to files:
crates/mdk-core/CHANGELOG.mdcrates/mdk-storage-traits/CHANGELOG.mdcrates/mdk-uniffi/CHANGELOG.mdcrates/mdk-sqlite-storage/CHANGELOG.md
📚 Learning: 2025-12-03T10:18:10.432Z
Learnt from: CR
Repo: marmot-protocol/mdk PR: 0
File: .cursor/rules/rust.mdc:0-0
Timestamp: 2025-12-03T10:18:10.432Z
Learning: Applies to **/*.rs : Place all `use` statements at the top of the file following the specified import order, never inside functions, methods, or other blocks
Applied to files:
crates/mdk-sqlite-storage/src/groups.rs
📚 Learning: 2026-01-03T08:39:39.799Z
Learnt from: CR
Repo: marmot-protocol/mdk PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-03T08:39:39.799Z
Learning: Applies to **/*.rs : Place all `use` statements at file top, never inside functions
Applied to files:
crates/mdk-sqlite-storage/src/groups.rs
📚 Learning: 2025-12-03T10:18:10.432Z
Learnt from: CR
Repo: marmot-protocol/mdk PR: 0
File: .cursor/rules/rust.mdc:0-0
Timestamp: 2025-12-03T10:18:10.432Z
Learning: Applies to **/*.rs : Use inline `mod tests { #[cfg(test)] ... }` for test modules rather than creating separate `tests.rs` files
Applied to files:
crates/mdk-sqlite-storage/src/groups.rs
📚 Learning: 2026-01-03T08:39:39.799Z
Learnt from: CR
Repo: marmot-protocol/mdk PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-03T08:39:39.799Z
Learning: Applies to **/*.rs : Define modules in separate files (`mod x;` with `x.rs`), not inline; exception: `#[cfg(test)] mod tests { }` and `#[cfg(bench)] mod benches { }` are inline
Applied to files:
crates/mdk-sqlite-storage/src/groups.rs
📚 Learning: 2025-12-03T10:18:10.432Z
Learnt from: CR
Repo: marmot-protocol/mdk PR: 0
File: .cursor/rules/rust.mdc:0-0
Timestamp: 2025-12-03T10:18:10.432Z
Learning: Applies to **/*.rs : Define sub-modules using separate files (e.g., `x.rs`) with `mod x;` declaration rather than inline `mod x { .. }` blocks, except for `tests` and `benches` modules
Applied to files:
crates/mdk-sqlite-storage/src/groups.rs
📚 Learning: 2025-12-03T10:18:10.432Z
Learnt from: CR
Repo: marmot-protocol/mdk PR: 0
File: .cursor/rules/rust.mdc:0-0
Timestamp: 2025-12-03T10:18:10.432Z
Learning: Applies to **/*.rs : Use inline `mod benches { #[cfg(bench)] ... }` for benchmark modules rather than creating separate `benches.rs` files
Applied to files:
crates/mdk-sqlite-storage/src/groups.rs
📚 Learning: 2025-12-03T10:18:10.432Z
Learnt from: CR
Repo: marmot-protocol/mdk PR: 0
File: .cursor/rules/rust.mdc:0-0
Timestamp: 2025-12-03T10:18:10.432Z
Learning: Applies to **/*.rs : Use relative `self::` imports for sub-modules rather than bare module names
Applied to files:
crates/mdk-sqlite-storage/src/groups.rs
🧬 Code graph analysis (1)
crates/mdk-sqlite-storage/src/groups.rs (4)
crates/mdk-memory-storage/src/groups.rs (1)
messages(54-91)crates/mdk-storage-traits/src/groups/mod.rs (4)
messages(110-114)limit(44-46)offset(49-51)new(39-41)crates/mdk-sqlite-storage/src/db.rs (5)
row_to_message(210-255)from(24-26)from(30-32)from(66-68)from(72-74)crates/mdk-sqlite-storage/src/lib.rs (3)
new_in_memory(120-148)from_slice(49-54)new(78-112)
⏰ 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). (1)
- GitHub Check: Test Coverage
🔇 Additional comments (7)
crates/mdk-core/CHANGELOG.md (1)
30-31: LGTM — PR references are correct and properly formatted.All pagination-related entries in mdk-core use the correct PR numbers (#111 for messages, #110 for welcomes) and follow the required markdown link format. The entries clearly document the pagination feature addition across Breaking changes, Added, and Fixed sections.
Also applies to: 51-52, 71-71
crates/mdk-sqlite-storage/CHANGELOG.md (1)
30-31: LGTM — PR references are correct and consistent with other crate CHANGELOGs.All pagination-related entries follow the required format and correctly reference PR #111 for messages pagination and PR #110 for welcomes pagination. This aligns with the references used in
crates/mdk-core/CHANGELOG.md.Also applies to: 35-36, 46-46, 51-52
crates/mdk-storage-traits/CHANGELOG.md (1)
30-31: LGTM!The changelog entries are well-structured with proper PR references, clear categorization (Breaking changes, Added, Fixed), and the security fix is appropriately documented. Based on learnings, all entries correctly include PR references in the required format.
Also applies to: 37-38, 44-44
crates/mdk-sqlite-storage/src/groups.rs (4)
8-8: LGTM!Import correctly adds
MAX_MESSAGE_LIMITandPaginationalongsideGroupStoragefrom the traits crate.
158-203: LGTM! Solid pagination implementation.The implementation correctly:
- Validates limit against
MAX_MESSAGE_LIMITto prevent memory exhaustion (addressing the security audit issue)- Defaults to
DEFAULT_MESSAGE_LIMITwhen pagination isNone- Verifies group existence before querying
- Uses parameterized SQL with
LIMIT/OFFSETfor safe, bounded retrieval- Returns messages in newest-first order (
ORDER BY created_at DESC)This aligns well with the in-memory reference implementation in
mdk-memory-storage.
326-329: LGTM!Test-specific imports are correctly placed at the top of the
mod testsblock, keeping them scoped to test code while following the guideline to not place imports inside functions.
455-579: Excellent test coverage!This test comprehensively validates the pagination implementation:
- Multi-page retrieval with correct ordering (newest first)
- No overlap between pages
- Default pagination behavior
- Error paths (invalid limits, non-existent group)
- Edge cases (offset beyond data, large offset values)
The test structure makes it easy to verify the pagination logic matches the expected behavior.
Summary by CodeRabbit
New Features
Bug Fixes
Breaking Changes
✏️ Tip: You can customize this high-level summary in your review settings.