Defines cursor pagination with stable ordering, scoped opaque cursors, bounded page sizes, mutation semantics, errors, and navigation links for APIs where offset drift is unacceptable.
Package status: reference context ready for human review. The contract and test scenarios are complete, but no claim is made that an adopting implementation has passed them.
Decision
Use an opaque cursor encoding the normalized query scope and last stable sort tuple. Sort by a domain key plus unique immutable tie-breaker; reject cursors reused with another filter, tenant, direction, or incompatible version.
Scope
- Request fields, stable ordering, cursor payload, forward/backward navigation, page limits, response links, errors, and data mutation behavior.
- Collection APIs backed by an orderable snapshot or keyset query.
- Security and operational behavior of cursor handling.
Outside this block
- Random page-number access or exporting an entire collection.
- Guaranteeing a transactionally frozen snapshot unless a snapshot token is part of the profile.
Contract
- The order contains a unique tie-breaker and defines null ordering.
- The cursor is opaque to clients and integrity-protected; it binds normalized filters, tenant, visibility, sort, direction, and cursor schema version.
- Requested limit is clamped or rejected according to policy and the applied limit is returned.
nextandpreviouslinks are absent when unavailable rather than present with null credentials.- Insertions and deletions follow documented live-view semantics; an optional snapshot mode names its expiry.
- Malformed, expired, mismatched, and unsupported-version cursors produce distinct machine codes without leaking cursor internals.
Implementation guidance
- Encode the last sort values, tie-breaker, scope digest, direction, issue time, and optional snapshot watermark.
- Perform keyset predicates consistent with order and null rules.
- Fetch one extra authorized row to determine whether another page exists.
- Keep cursor signing keys rotatable with a key ID and bounded acceptance window.
Failure handling and safeguards
- If a referenced row was deleted, keyset values still advance without requiring that row to exist.
- If cursor scope differs, reject rather than silently starting a new listing.
- If the snapshot expires, return a restart instruction and never reuse the cursor as a live-view cursor.
Verification and operations
- Measure page-size distribution, cursor validation failures, expired snapshots, duplicate/missing reports, query latency, and rows scanned.
- Test equal sort values, nulls, deletions, insertions, direction reversal, and authorization changes.
- Version cursor behavior before changing sort or normalization.
The executable-looking examples in this package are fixtures and acceptance contracts. Run
python tools/validate.py from the collection root to check package structure and metadata; then
implement and execute the scenarios in the target repository.
Adoption assumptions
- Names and numeric values in
example.yamlare an adoption profile, not universal defaults. - The adopting team must map actors, data classes, error vocabulary, and ownership to its system.
References
- No external normative source is required; this package defines a project decision.