# Trim lookahead and bind backward keyset cursors

A procedure for chronologically presented backward keyset pages, including correct overflow trimming and opaque authenticated continuation tokens bound to the authorized view.

Exact reference: {"kind":"skill_version","skill_id":"skl_I5SzMY4gyMyRvZWt5sBXOw","version_id":"skv_pDvSq6wovckewwzhSnUY0A"}

Applicability: []

# Backward keyset pages displayed chronologically

Use this when an endpoint fetches older records using a unique ascending key, queries them in descending order with one lookahead row, and returns each page in ascending order. Treat every continuation cursor received from a client as untrusted.

## Page boundary invariant

The visible page contains the newest `limit` records below the incoming exclusive boundary within one authorized thread, normalized filter set, direction, and snapshot. The lookahead record proves whether an older record remains but is not returned. The next boundary is the oldest **visible** record, not the lookahead record.

1. Query the pinned view with a strict older-than comparison against the previous visible boundary, ordered by key descending, taking `limit + 1` rows.
2. Set `has_more` when more than `limit` rows are fetched.
3. Retain the first `limit` descending rows and discard the possible final lookahead row.
4. Reverse the retained rows for chronological presentation.
5. If `has_more`, issue a cursor whose boundary is the key of the first presented row. Otherwise omit the continuation cursor.

If the result is reversed before trimming, remove the *first* reversed row. Removing the last reversed row discards the newest visible record.

## Bind and conceal the continuation cursor

At the first page, resolve and authorize the requested view. Canonicalize filters so defaults, ordering of set-valued options, nulls, and types have one representation. Select a stable snapshot identity. Keep the normalized thread scope, filter representation or digest, direction, snapshot identity, page limit, oldest visible key, principal or tenant binding, format version, and expiry in the cursor claims.

Protect the claims with authenticated encryption, or keep them in immutable server-side state addressed by a random opaque handle and MAC-sign that handle. A signature over readable encoded claims gives integrity but not confidentiality; it does not hide internal identifiers. Include key rotation and expiry in the token format. Never put raw internal identifiers in a readable token or diagnostic message.

For each continuation, authenticate and decode the token, enforce expiry, independently resolve the current requested thread and filters, recheck authorization, and compare the bound principal or tenant, thread, canonical filters, direction, snapshot identity, and page limit. Use the authenticated snapshot and boundary for the query only after all checks pass. Return one generic invalid-cursor response on malformed, expired, or mismatched cursors; do not report the internal values that differed. The UI should clear its cursor when the view changes, while the server remains the enforcement point.

A cursor-carried snapshot pins subsequent pages to the original view. If the protocol must reject a cursor from a previously opened view, compare it with an independently supplied and authenticated active view generation or maintain that generation server-side. A stateless cursor alone cannot distinguish replay of an old but otherwise matching view before expiry.

A maximum sequence fence prevents newly appended records from entering later pages. It is a full membership snapshot only when relevant records and filter fields are immutable. For mutable feeds, use versioned as-of reads or materialize snapshot membership.

## Finite check

With keys 1 through 8 and a page limit of 3, the first descending query returns `8, 7, 6, 5`. Retain `8, 7, 6`, present `6, 7, 8`, and continue below key 6. The next query returns `5, 4, 3, 2`; present `3, 4, 5` and continue below key 3. The last query returns `2, 1`; present `1, 2` and provide no continuation. A cursor minted for that first view must fail when presented with a different thread, normalized filters, direction, or independent snapshot identity.

The ordering proof assumes a unique total order and a strict exclusive comparison. Use a composite key with a deterministic tie breaker if the primary sort key is not unique. The example is a reasoning check, not an executed test.

## Supporting basis and limitations

The ordering behavior is shown by finite enumeration. The cursor requirements follow from treating client state as untrusted and comparing authenticated server-issued claims with independently derived request context. No implementation or runtime tests were executed.

## Change and rationale

Extends the pagination procedure with context-bound opaque cursors, mismatch rejection, snapshot guarantees, and replay limits.

A cursor that contains only the last sequence can silently cross thread, filter, direction, or snapshot boundaries. A readable signed payload may also expose identifiers. The extension preserves the existing pagination invariant while defining validation and confidentiality requirements.
