跳转到内容

ak.schema.read_cursor.v1

← Schemas

Arkret Read Cursor
ak.schema.read_cursor.v1 · file: schemas/read-cursor.schema.json

Actor-private multi-device read cursor. Stored in encrypted account data or actor-private stream; NEVER published into a shared Realm Event history. Bound to (actor_id, realm_id, read_scope, position, HLC, device_id) per zh/discovery/read-receipts.md §6.6. Multi-device merge is causal-first per §6.5: the position that causally dominates wins regardless of HLC; HLC max decides only causally concurrent positions, and device_id only breaks equal HLCs. The object carries NO typed id and NO update timestamp. A cursor is never updated in place: its identity is the (actor_id, realm_id, read_scope) tuple, each advance is a freshly authored ak.read_cursor.advance Event (event-kind-registry id_source=not_an_object_id), and the time of that update is that Event envelope created_at. There is no read_cursor typed ID kind, no id-addressed read surface, and capability selectors address read cursors only as read_cursor:<realm>:*. Derived views that need a time (read-cursor-operations.schema.json#/$defs/read_marker_outcome, the ak.read_cursor.update device-message content ak.schema.read_cursor_update.v1) take updated_at from the winning advance envelope.

* $ · object
Actor-private multi-device read cursor. Stored in encrypted account data or actor-private stream; NEVER published into a shared Realm Event history. Bound to (actor_id, realm_id, read_scope, position, HLC, device_id) per zh/discovery/read-receipts.md §6.6. Multi-device merge is causal-first per §6.5: the position that causally dominates wins regardless of HLC; HLC max decides only causally concurrent positions, and device_id only breaks equal HLCs. The object carries NO typed id and NO update timestamp. A cursor is never updated in place: its identity is the (actor_id, realm_id, read_scope) tuple, each advance is a freshly authored ak.read_cursor.advance Event (event-kind-registry id_source=not_an_object_id), and the time of that update is that Event envelope created_at. There is no read_cursor typed ID kind, no id-addressed read surface, and capability selectors address read cursors only as read_cursor:<realm>:*. Derived views that need a time (read-cursor-operations.schema.json#/$defs/read_marker_outcome, the ak.read_cursor.update device-message content ak.schema.read_cursor_update.v1) take updated_at from the winning advance envelope.
* schema · const "ak.schema.read_cursor.v1"
enum: "ak.schema.read_cursor.v1"
* actor_id · oneOf[2] · $ref ./common-ids.schema.json#/$defs/actor_id
Complete protocol identity for an Event author or Realm member: account carries the exact AccountId for every Station-hosted principal; service identifies a service acting as itself. The discriminator is validated against accepted registration and admission evidence; it never authorizes itself. Account and service are distinct, and no comparison may fall back to a bare principal_id. Agent and integration classification, provisioning, controller binding and credential authorization are independently verified facts, not identity variants. Account actors at different Stations MUST NOT share or inherit authority merely because their principal_id, DID controller or signing key matches, including membership, capability, RealmCommit-signing and recovery authority.
oneOf · oneOf[0] · object
* kind · const "account"
enum: "account"
* account_id · $ref #/$defs/account_id · $ref #/$defs/account_id
oneOf · oneOf[1] · object
* kind · const "service"
enum: "service"
* service_id · $ref #/$defs/did_core_id · $ref #/$defs/did_core_id
* device_id · string
Originating device. Required for multi-device convergence: when two devices race on the same (actor_id, realm_id, read_scope) the receiver first picks the later position when both position Events are committed on the same CommitStreamRef; only when neither dominates (different streams or the same Event) does it pick the greater HLC, and device_id provides a deterministic actor_id-scoped tiebreaker when those HLCs are equal. device_id MUST NOT be used to select a position that another position dominates.
pattern: ^ak:device:[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$
* realm_id · string · $ref ./common-ids.schema.json#/$defs/realm_id
Retyped ak.realm.create Event token. It therefore carries the same fixed current-v1 0x01/SHA-256 content-address identity and is not selected by Realm state.
pattern: ^ak:realm:[A-Za-z0-9_-]{44}$
* read_scope · object · $ref #/$defs/read_scope
allOf · allOf[0] · ?
allOf · allOf[1] · ?
allOf · allOf[2] · ?
allOf · allOf[3] · ?
allOf · allOf[4] · ?
* kind · string (enum)
Read-scope object kind per zh/models/private-objects.md §2.2. Track-scoped read cursors use kind='strand' plus track_name. Thread cursors use kind='thread' with container_ref = the thread root message id (zh/discovery/read-receipts.md §5); thread is a projection selector over the reply sub-timeline, not a first-class protocol object. Shares the read-scope discriminator family with read-receipt.schema.json; each schema declares its own supported subset.
enum: "realm" "circle" "space" "strand" "thread"
container_ref · string
Required when kind is circle, space, strand, or thread; omitted for kind='realm'. Thread read scopes reference the root message.
pattern: ^ak:(circle|space|strand|message):[A-Za-z0-9_-]{44}$
track_name · string
Required when kind='strand' AND the cursor scopes a single track (e.g. 'discussion', 'synthesis', or any profile-registered track name). Track names MUST match the Strand's registered tracks; unknown tracks MUST be rejected with schema_violation.
pattern: ^[a-z][a-z0-9_]{0,63}$
* position · object · $ref #/$defs/position
* event_id · string
pattern: ^ak:event:[A-Za-z0-9_-]{44}$
* hlc · string
pattern: ^[0-9a-f]{12}-[0-9a-f]{4}-[0-9a-f]{8}$

Source