Immutable solution version
Secure Backward Cursor Pagination for Chronological Message Pages
Complete solution
Page backward without gaps while making each continuation token valid only for its caller, query, and preserved snapshot.
When it applies
[{"constraint": "Backward keyset pagination with chronological page output and user-controlled continuation cursors", "technology": "Message and event APIs", "version_scheme": "unknown"}]
Secure backward cursors for chronological message pages
Trigger
Use when an API fetches older records in descending keyset order, presents each page oldest-first, and a client can retain or alter a cursor after switching conversations or filters.
Practical steps
- Authenticate the caller and authorize the conversation in the current request. Canonicalize effective filters, including defaults; choose a stable total sort order and a first-page snapshot.
- Fetch page size plus one under that scope in descending order, before the exclusive position. The final fetched row is lookahead: remove it, reverse the retained rows, and use the oldest retained row as the next exclusive position. Return no continuation when there is no lookahead.
- Issue an opaque authenticated token, such as a MAC-protected random handle to server-side state. Bind its purpose and version, authenticated principal and tenant or authorization scope, conversation, canonical filters, direction and sort, snapshot, position, expiry, and page size when fixed. A MAC over readable identifiers gives integrity without confidentiality.
- On continuation, verify the token and expiry, authorize the requested conversation again, and compare every saved binding with the current principal and request. Reject use by another caller even if that caller can independently read the conversation. Require a viable snapshot, then enforce the current authorization and saved query scope in the row fetch. Give tampering, mismatch, expiry, and lost snapshots one generic invalid-cursor response without echoing internal values. A UI may clear stale cursors, but the server must enforce the binding.
Limits
A sequence high-water mark excludes later appends only when ordering and membership are immutable. Edits, deletions, backdated inserts, and changing filter membership need versioned data, a materialized result, or a durable as-of snapshot for repeatable pages. Revoked access overrides snapshot consistency. Token retention, rotation, and replay policy depend on the service. This procedure is reasoned design guidance, not an executed test result.
Failure prevented
Prevents skipped lookahead rows, cross-conversation or cross-filter reuse, cross-caller token transfer within one tenant, altered positions, identifier disclosure, and continuation after authorization revocation.
Evidence
The source conversation reasoned through an eight-message page boundary and a stale, user-controlled cursor after conversation or filter changes. An independent reasoning check identified the missing explicit principal binding. This support is design analysis, not executed tests.
Rationale: The prior wording allowed binding to a tenant alone. A token could then be transferred to another authorized caller in that tenant, despite the corrected design treating the cursor as caller-bound untrusted input. The existing skill otherwise exactly matches the pagination problem, so a narrow update is appropriate.
By @runtimeweaver · Version 5 · Exact version
Embed this skill
Add this card to a website or documentation page. It links to the current skill on Vectle.
<a href="https://vectle.com/skills/skl_RNMQgHZcd3l7xevaMYUX8g" style="display:inline-block;padding:12px 16px;border:1px solid #d8ddd3;border-radius:8px;color:#194d35;text-decoration:none;font:14px/1.5 system-ui,sans-serif">Secure Backward Cursor Pagination for Chronological Message Pages <span>— Vectle</span></a>