# Design Context-Bound Opaque Pagination Cursors
Use this pattern when an API accepts a client-controlled continuation cursor and the same endpoint can serve multiple scopes, filters, directions, or consistency snapshots.
## Core invariant
A cursor is an authenticated continuation contract, not just an encoded row key. It must bind:
- the authorized resource scope, represented by an opaque binding rather than a raw internal identifier;
- the canonical semantic filter set, including explicit defaults;
- traversal direction and ordering version;
- the snapshot or dataset revision selected by the initial request;
- the exclusive ordering boundary for the next page;
- token version, audience, expiry, and key identifier;
- the tenant or principal scope when cross-principal replay must be prevented.
For descending storage reads that return each page chronologically, fetch page size plus one, remove the oldest overflow row while still descending, reverse the retained rows, and set the next exclusive boundary to the oldest retained row. Never use the discarded overflow row as an exclusive cursor.
## Confidential token formats
A signature prevents alteration but does not hide a readable payload. To avoid exposing internal identifiers, use one of these forms:
1. A sealed token using an authenticated-encryption construction, with endpoint and format version as associated data.
2. A signed high-entropy random handle whose state is stored server-side.
Do not place raw database identifiers in a merely base64-encoded signed payload. Support key rotation with an allowlisted key identifier and reject unknown algorithms or keys.
## Canonical query binding
Canonicalize semantic filters on the server before hashing or binding them. Apply defaults, normalize equivalent values, sort unordered sets and object keys, and include the query-schema version. Presentation-only parameters should not affect the binding. Page size may be bound when changing it would alter the continuation contract.
## Initial request
1. Authenticate the caller and authorize the requested resource.
2. Canonicalize filters and select the traversal direction.
3. Establish a stable snapshot. An append-only feed may use a monotonic high-water mark; mutable data requires an MVCC revision or server-side snapshot handle.
4. Query within that snapshot in storage order with an extra row.
5. Trim the oldest overflow row, reorder the retained rows for the response, and issue a sealed cursor whose boundary is the oldest retained row.
## Continuation request
1. Apply strict token size and format limits.
2. Authenticate and, when applicable, decrypt the cursor before trusting its contents.
3. Re-authorize the current caller and requested resource. Possessing a cursor never grants access.
4. Derive the expected opaque scope binding and canonical filter binding from the current request.
5. Compare scope, filters, direction, audience, ordering version, and any explicit snapshot input against the cursor. Validate expiry and snapshot availability.
6. On any mismatch, return a single generic invalid-cursor response and advise restarting pagination. Do not echo tokens, internal identifiers, expected bindings, or mismatch details.
7. Query using the cursor snapshot and an exclusive boundary, then issue the next cursor with unchanged bindings and the new oldest-retained boundary.
Use constant-time comparison for authentication tags and fixed-size secret-derived bindings. Record bounded internal reason codes and a correlation identifier in protected telemetry, but never log the raw token.
## Snapshot caveat
A maximum sequence freezes later inserts but does not freeze edits, deletions, or filters whose truth can change. When those mutations matter, use a real revisioned snapshot or materialized server-side result state. Expired or unavailable snapshots should fail as a generic invalid cursor rather than silently continuing against live data.
## Client behavior
The client should clear its cursor whenever resource scope, semantic filters, direction, or sort order changes. This is a usability measure only; the server must still enforce every binding because the cursor is user-controlled.
## Validation cases
Verify normal multi-page traversal, tampering, reuse on another resource, filter changes, direction changes, snapshot override, expiry, key rotation, authorization revocation, concurrent inserts, mutable records, and final-page behavior. Assert no gaps or duplicates within a snapshot and no disclosure in errors or logs.