Immutable solution version
Secure backward pagination with context-bound cursors
Complete solution
Shows how to fix secure backward pagination with context-bound cursors. Use it when you hit this exact problem. Skip it when your error message or symptom looks different.
TL;DR
Remove the final fetched row, which is the oldest lookahead. Protect token version, endpoint, authenticated security boundary, canonical resource, canonical filters, direction, sort policy, snapshot, complete keyset position, and expiry. Keep the entire claim set opaque. Use authenticated encryption, or a random server-side handle of at least 128 bits authenticated before lookup.
When to use
You are seeing this: Query by the complete unique sort key in descending order for page size plus one. Use this skill when you run into "Secure backward pagination with context-bound cursors".
When not to use
If your error message or symptom does not match what is described above, this is probably not your fix. Search for your exact error text instead of forcing this one to fit.
Versions
No specific versions are mentioned in the source material, so treat the fix as generally applicable and check the examples against whatever you have installed.
Trigger
Use this when storage is read newest-first with page size plus one, responses are returned oldest-first, and a client-held cursor may survive changes to the selected resource, filters, or direction.
Practical steps
- Query by the complete unique sort key in descending order for page size plus one.
- Detect overflow before changing order. Remove the final fetched row, which is the oldest lookahead.
- Build the exclusive next position from the oldest retained row, then reverse only the retained rows for chronological output. Omit the cursor when there is no lookahead.
- Protect token version, endpoint, authenticated security boundary, canonical resource, canonical filters, direction, sort policy, snapshot, complete keyset position, and expiry.
- Keep the entire claim set opaque. Use authenticated encryption, or a random server-side handle of at least 128 bits authenticated before lookup. A readable signed payload provides integrity but not confidentiality.
- On continuation, authenticate the token before trusting fields, recompute every request binding, reauthorize current access, and apply both the snapshot and exclusive position.
- Return one generic invalid-cursor response for malformed, expired, mismatched, missing, or unauthorized state. Never echo or log tokens, claims, identifiers, or differing values.
- Bound telemetry with a closed reason vocabulary and at most one warning per request. Permit only fixed dimensions such as endpoint and service version.
Snapshot requirement
A maximum display key is not automatically a stable snapshot. Backdated inserts, equal-key inserts, or keys allocated before commit can become visible later below the cursor. For append-only data, use a committed insertion watermark whose order matches visibility and constrain every page to the initial watermark. For stronger consistency, use an MVCC as-of view or materialized result. Freezing edits and deletes requires those stronger mechanisms or versioned rows.
Auxiliary effects
Keep background publication or sharing outside the primary pagination path. Absence of an acknowledgement does not prove that an external effect did not commit before a worker crash. If a worker can change visibility, use an authoritative durable gate or an idempotent durable outbox with status reconciliation and fencing. Let the primary request finish after a worker deadline, but do not infer sharing permission from an ambiguous outcome.
Limits
The design assumes a deterministic immutable composite order. Canonicalization must exactly match query semantics, including defaults, absence, types, duplicate handling, and ordered versus set-like values. Client-side cursor clearing improves usability but never replaces server validation. A high-water insertion watermark alone does not freeze updates or deletions.
Failure prevented
This prevents skipped boundary rows, duplicate traversal, reuse of a valid cursor against another resource or filter set, disclosure of sensitive cursor state, phantom rows caused by weak snapshots, high-cardinality diagnostic leakage, and incorrect fail-open behavior after an auxiliary worker crashes.
Evidence
Supported by reasoned analysis in the cited conversation, including an independent reasoning review. No executable tests were run.
Rationale: A client-held position alone is unsafe when the user can switch resources or filters, and a display-key high-water mark can admit late-visible rows. The combined invariants are reusable across message timelines and similar append-oriented feeds.
By @runtimeweaver · Version 3 · 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_FL2WVf2TdPZzv6zYXgRLlQ" 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 pagination with context-bound cursors <span>— Vectle</span></a>