Single opaque cursor type. Used as both stream continuation token (incremental sync, list pagination — 'after', 'before', 'prev_cursor', 'next_cursor') and barrier token (read-your-writes wait — 'X-Arkret-Wait-For'). Wire form is always ak:cursor:<base64url(canonical_json)>. Clients MUST treat the wire form as opaque; only the issuing server reads the inner structure.
Integrity contract (normative, see client-sync.md §12 and encoding.md §8 / §8.3.1): v1 core uses the stateful opaque-handle form only — body is {v, purpose, issued_at, expires_at, h} where 'h' is an unguessable server-side handle that the issuing server resolves to the bound (account_id, device_id, filter_digest, purpose, positions, target?, expiry) tuple. account_id is the complete exact AccountId and already contains the Station coordinate; an additional principal_id or service_id identity sidecar MUST NOT be used. Server-side handle lookup IS the integrity check; there is no inline transcript binding to verify on the client side. Servers MUST reject unknown / expired / cross-binding handles with cursor_integrity_invalid.
* purpose ·
string (enum)Cursor purpose. 'stream' = continuation cursor for incremental sync / list pagination. Used as 'after' on /account/subscribe and as response 'prev_cursor' (older direction) / 'next_cursor' (newer direction) on the list-pagination surfaces. It is NOT used by ak.self.committed_event.read.scan.v1 or ak.peer.committed_event.read.scan.v1: positional stream scan paginates by stream_position, and those surfaces MUST reject an ak:cursor: value (api-conventions.md section 7.2). prev_cursor / next_cursor carry absolute directional meaning regardless of how the request was issued — see api-conventions.md §7.0. 'barrier' = read-your-writes / wait-for-event cursor (use in X-Arkret-Wait-For header to block until local checkpoint covers target.event_id). The handle binding (resolved server-side) carries positions for 'stream' or target for 'barrier'; the wire body does not.
enum: "stream" "barrier"
* issued_at ·
string (date-time) · format=date-time · $ref #/$defs/timestampCanonical Arkret-owned absolute instant. UTC Z form with exactly three millisecond digits. Whole seconds MUST use .000Z; offsets, missing/finer fractions, lowercase separators, leap seconds, and invalid Gregorian calendar dates are forbidden. Shape validation by this pattern is supplemented by semantic date validation.
pattern: ^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])T([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]\.[0-9]{3}Z$
* expires_at ·
string (date-time) · format=date-time · $ref #/$defs/timestampCanonical Arkret-owned absolute instant. UTC Z form with exactly three millisecond digits. Whole seconds MUST use .000Z; offsets, missing/finer fractions, lowercase separators, leap seconds, and invalid Gregorian calendar dates are forbidden. Shape validation by this pattern is supplemented by semantic date validation.
pattern: ^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])T([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]\.[0-9]{3}Z$
* h ·
stringServer-side opaque handle. The issuing server MUST bind this handle to (account_id, device_id, filter_digest, purpose, positions, target?, expiry) and MUST be able to revoke it. account_id is the complete exact AccountId and already contains the Station coordinate; an additional principal_id or service_id identity sidecar MUST NOT be used. The handle MUST be unguessable (≥ 128 bits entropy after base64url decoding); minLength=22 enforces that floor mechanically (base64url packs 6 bits per character so 128/6 ≈ 21.33 → at least 22 chars of the [A-Za-z0-9_-] alphabet). Servers MUST reject any unknown / expired / cross-binding handle with `cursor_integrity_invalid`. Handle lookup IS the integrity check — there is no MAC or signature to verify inline. This is the moral equivalent of Matrix `next_batch` / MSC4186 `pos` and satisfies the integrity contract without crypto. Servers SHOULD keep the handle → binding map durable across process restarts (see client-sync.md §12.1): a memory-only table is still integrity-correct (unknown handles fail closed) but turns every restart into a fleet-wide forced full resync. For production-grade deployment profiles (small_team / organization / high_security_organization / sovereign_deployment / sovereign_enclave / isolated_sovereign_network) this durability plus TTL GC is a MUST per client-sync.md §12.1; only personal_node keeps it at SHOULD.
pattern: ^[A-Za-z0-9_-]+$
(^_[A-Za-z0-9_]*$) ·
?Server-private auxiliary fields (e.g. `_compress` / `_kid` flags). Names MUST start with underscore. In v1 core there is no `_mac` / `_sig` integrity field — handle lookup IS the integrity check — so the underscore namespace is private to the issuing server. `_mac` and `_sig` themselves are explicitly excluded by the top-level `not` clause: a cursor body carrying either MUST be rejected (encoding.md §8.3).