# Secure backward pagination with scoped opaque cursors

Keep chronological backward pages gap-free while binding opaque cursors to the authorized conversation, filters, direction, and stable snapshot.

Exact reference: {"kind":"skill_version","skill_id":"skl_WBoLRIJ1UVflsR1Y_dMcwA","version_id":"skv_neJEajWxgFkAcQYkqayh-g"}

Applicability: []

# Secure backward pagination with scoped opaque cursors

## Trigger

Use this when an API scans toward older records in descending order, fetches one lookahead row, presents each page chronologically, and accepts a cursor that a client may reuse after changing conversations or filters.

## Failure prevented

This procedure prevents skipped or repeated records, reuse of a cursor for another query or caller, disclosure of internal identifiers, and pages that silently mix snapshots.

## Practical steps

1. Establish an immutable total order. Add a unique tie breaker when sequence values can coincide, and use the same ordering tuple for the query, snapshot boundary, and exclusive resume condition. Accept only a positive, bounded page size.
2. On the first request, authenticate and authorize the requested conversation. Resolve defaults and access restrictions before canonicalizing effective filters. Choose a snapshot that can preserve record contents and filter membership for the cursor lifetime.
3. Apply conversation scope, effective filters, current row-level authorization where needed, snapshot, and exclusive older-than position before ordering and limiting the query. Fetch page size plus one rows in descending order. If a lookahead exists, discard the final descending row, reverse the retained rows, and set the next position to the oldest retained row. Issue no next cursor without lookahead.
4. Give the client a signed opaque random handle. Keep an immutable server-side record binding endpoint purpose and format, caller and authorization scope, internal conversation, canonical filter digest, backward direction, original snapshot, ordering definition, page size, exclusive position, and expiry. Signing readable identifiers prevents alteration but does not hide them. Authenticated encryption can replace server-side state when a stateless design is required.
5. For continuation, bound token parsing, verify its signature and expiry, and load its state. Independently authorize the conversation named by the current request and derive its effective filters and scope again. Reject a mismatch before querying; never let cursor state select the conversation or grant access. Recheck access to every returned row if permissions can vary by record. If the original snapshot cannot be read, reject the continuation.
6. Return one generic restart-pagination error for invalid, expired, unauthorized, wrong-context, or unavailable-snapshot cursors. Keep internal identifiers and raw cursor values out of responses and logs. Mint a fresh immutable handle for each continuation, retaining all bindings and changing only the exclusive position.

With sequences one through eight and page size three, the first descending fetch is eight, seven, six, five. Discard five, return six, seven, eight, and resume strictly below six. Five remains available to the next request.

## Limits and support

A high-water sequence bound is a complete snapshot only for a suitable immutable, monotonic stream. Edits, deletions, backfills, or changed filter membership require revisioned point-in-time reads or a materialized snapshot. Cursor expiry must fit within snapshot retention.

The support is a worked sequence trace, security reasoning, and independent reasoning review. No implementation, database query, or executable tests were run.

## Supporting basis and limitations

The cited conversation contains a reasoned eight-record pagination trace and a design for context-bound signed opaque cursors. A separate native reasoning review checked the design and identified the added conditions. This is reasoned analysis, not executed testing.

## Change and rationale

Make pre-limit query scoping and row-level authorization explicit, require bounded page size, and reject continuation when the bound snapshot is unavailable.

The current skill covers the same pagination and cursor threat model, so a focused update avoids overlapping guidance. Independent reasoning exposed conditions that its procedure did not state explicitly: authorization and filters must shape the limited query, and an unavailable snapshot must fail closed.
