# Secure Backward Cursor Pagination with Chronological Pages

Keep backward keyset pages gap free and prevent a user controlled cursor from crossing conversation, filter, direction, authorization, or snapshot boundaries.

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

Applicability: []

# Secure Backward Cursor Pagination with Chronological Pages

## Trigger

Use when an API fetches older records newest first with one overflow row, returns each page chronologically, and accepts a cursor that can outlive a UI change of conversation or filters.

## Practical steps

1. Choose a stable, unique ordering tuple. Authenticate the caller, authorize the requested conversation, canonicalize effective filters and defaults, fix backward direction, and establish a durable snapshot or as-of revision.
2. Fetch page size plus one matching rows in descending order within that snapshot. If an extra row exists, remove the final and oldest fetched row, then reverse the retained rows. Issue a cursor only when that extra row proves more data exists. Its exclusive boundary is the oldest returned row, never the removed row.
3. Bind the cursor to the authorized subject or tenant, resolved conversation, canonical filters, direction, ordering version, snapshot, boundary, purpose, and expiry. Authenticate it. Use authenticated encryption or a signed random handle backed by server state when internal identifiers or filter state must remain hidden; a readable signature alone provides no confidentiality.
4. On continuation, authenticate and validate the token before trusting its fields. Independently reauthorize the currently requested conversation and compare current bindings. The token normally defines the continuation snapshot. Compare a separate snapshot value only if the API explicitly accepts one. Verify snapshot availability, then query that same historical view using the exclusive older-than boundary.
5. Reject altered, expired, inaccessible, or context-mismatched cursors with one generic external error that reveals neither internal identifiers nor which binding failed. The client can clear the cursor and begin a new walk.

## Limits

A maximum-sequence cutoff substitutes for a full snapshot only when later inserts always receive strictly greater keys, existing records are append-only, and filter membership cannot change. Backfilled inserts with older keys, edits, deletions, or changing filters require temporal reads or a durable result-set snapshot. Expired snapshot state requires a fresh walk. Cursor authenticity never replaces authorization.

## Failure prevented

These steps prevent skipped boundary rows, false continuation, cross-conversation or cross-filter replay, direction confusion, snapshot drift, and identifier disclosure. This guidance is supported by reasoned sequence and security analysis, including an independent reasoning review; no executable tests were run.

## Supporting basis and limitations

The cited conversation establishes the exclusive page boundary by reasoned enumeration. The cursor binding and snapshot corrections follow from a further independent reasoning review of the same design. No implementation was inspected and no executable tests were run.

## Change and rationale

Clarifies that the authenticated cursor defines the continuation snapshot and that a sequence cutoff requires monotonic later inserts, not merely append-only storage.

This current skill is an exact topical match. Its existing snapshot wording can be read as comparing against a newly chosen snapshot, and its append-only condition omits backfilled inserts with older keys.
