# Secure Backward Cursor Pagination with Chronological Pages

Design backward keyset pagination that returns chronological pages while preventing cursor reuse across conversations, filters, directions, authorization scopes, ordering rules, or snapshots.

Exact reference: {"kind":"skill_version","skill_id":"skl_lnmhNWGOw5JqYwoJZZ7LNA","version_id":"skv_wVi53VNnjXtFffXNuvjbEg"}

Applicability: [{"constraint":"Backward pagination fetches one overflow record in descending order, presents retained records chronologically, and accepts a user-controlled cursor.","technology":"Keyset pagination","version_scheme":"unknown"}]

# Secure Backward Cursor Pagination with Chronological Pages

## Trigger

Use this guidance when an API reads older records in descending keyset order, fetches page size plus one to detect more data, returns each retained page chronologically, and accepts a cursor that may remain in a client after its conversation or filters change.

## Practical steps

1. Define a stable, unique ordering tuple. Include a deterministic tie-breaker when the primary sequence can collide.
2. Fetch page size plus one rows in descending order. When overflow exists, remove the final row before reversing. If reversal happens first, remove the first row instead.
3. Build the exclusive next boundary from the oldest retained row, not the discarded lookahead. Return retained rows in chronological order.
4. On the first page, establish a snapshot. A high-water ordering tuple works only for append-only records whose filter-relevant fields are immutable.
5. Seal an opaque cursor containing the boundary, snapshot, direction, format and ordering version, expiry, and bindings to the resolved conversation, canonical effective filters, and relevant tenant or authorization scope. Bind page size only when changing it during a walk is unsupported.
6. Use authenticated encryption or an opaque server-side handle. A clear signed payload prevents modification but can still reveal internal identifiers or sensitive filter state.
7. On reuse, authenticate the cursor before trusting its contents, reauthorize the requested conversation independently, canonicalize the current context, compare every binding, validate snapshot availability, and derive the query only from validated server-side values.
8. Reject malformed, expired, tampered, or context-mismatched cursors with one generic external error. Keep expected values, internal identifiers, and detailed mismatch reasons out of the response.

## Limits

A high-water mark excludes later inserts but does not freeze mutable filter membership, edits, or deletions. Those cases require versioned temporal reads or a durable result-set snapshot. Expired snapshot state should force a fresh pagination walk. Cursor authenticity never replaces authorization.

## Failure prevented

This prevents skipped or duplicated boundary rows, cross-conversation cursor replay, stale-filter pagination, direction confusion, snapshot drift, authorization-scope confusion, and disclosure of internal identifiers through cursor payloads or mismatch errors.

## Support

The supporting evidence is reasoned sequence analysis and security review. No executable implementation or tests were run.

## Supporting basis and limitations

Supported by a reasoned sequence trace over eight ordered records and a security analysis of cursor reuse and snapshot behavior. No tests were executed.

## Change and rationale

Creates focused guidance for overflow trimming, exclusive boundary selection, opaque authenticated cursor binding, validation, authorization, and snapshot limits.

A bounded guidance search found no matching current skill to update. The design is reusable for message and event APIs where user-controlled cursors can survive UI context changes.
