Skip to content

XProtocol Graph Store — Full Specification

Protocol Version: 0.2 (Draft) Date: 2026-05-31 License: CC BY 4.0 (this document) · Apache 2.0 (code) Status: Proposed — not yet ratified as a community standard Namespace: xp.graph.* Depends on: XProtocol-Specification.md (core protocol), XProtocol Event Store (xp.store.*) Repository: gitlab.com/xprotocol/xprotocol-graph-store (proposed)


Abstract

The XProtocol Graph Store extends the XProtocol Event Store with a structured annotation system that transforms the flat event collection into a navigable, queryable graph. Every stored event can carry a mutable, authorized, auditable dictionary of typed metadata — linking it to other events, grouping it with other entities, tagging it for workflows, tracing it across systems, and organizing it for recipients — without ever touching the immutable signed payload.

The result is a data substrate that simultaneously serves as a document store, a graph database, a distributed tracing system, an observability platform, a workflow engine, and an AI agent memory layer — all under a single cryptographic identity model, with provable authorship and access control on every record and every mutation to every record.


1. Design Principles

1.1 — Immutability of Events, Mutability of Annotations

XProtocol events are cryptographically immutable. The signed payload, the sender key, the recipient key, the timestamp, and the content hash are permanent and unforgeable. Changing any of them invalidates the signature.

The Graph Store adds a second layer — the annotation envelope — that is explicitly mutable, explicitly unencrypted, and explicitly authorized. Annotations sit outside the signature boundary. The event proves what happened; the annotations describe how it relates to everything else.

┌─────────────────────────────────────────────────────┐
│  Stored Graph Node                                  │
│                                                     │
│  ┌─────────────────────────────────────────────┐    │
│  │  XpEvent (immutable, signed)                │    │
│  │  - id (SHA-256 content hash)                │    │
│  │  - sender, recipient, kind, timestamp       │    │
│  │  - payload (encrypted ciphertext)           │    │
│  │  - signature (Ed25519, 64 bytes)            │    │
│  └─────────────────────────────────────────────┘    │
│                                                     │
│  ┌─────────────────────────────────────────────┐    │
│  │  Annotation Envelope (mutable, authorized)  │    │
│  │  - thread.*       (conversation structure)  │    │
│  │  - recipient.*    (personal inbox state)    │    │
│  │  - trace.*        (distributed tracing)     │    │
│  │  - groups         (membership)              │    │
│  │  - links          (typed relationships)     │    │
│  │  - workflow.*     (process state)           │    │
│  │  - versioning.*   (supersession chain)      │    │
│  │  - ai.*           (semantic tags)           │    │
│  │  - custom.*       (arbitrary extensions)    │    │
│  └─────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────┘

1.2 — Every Mutation Is a Signed Event

Annotations are never silently mutated in place. Every annotation change is effected by sending an xp.graph.annotate event to the store — a signed event that itself becomes a permanent record. The annotation history for any stored event is therefore a first-class queryable dataset: who annotated this event, what they changed, and when.

This means the Graph Store has no back-door writes. Every state change is traceable, auditable, and attributable to a specific key.

1.3 — Authorization Is Per-Field, Not Per-Record

Every annotation key declares its own authorization level. Some fields can only be set by the sender. Some only by the recipient. Some by either participant. Some by explicitly granted third-party keys. Some only by the store itself. These authorizations are declared in the Annotation Schema for each event kind and enforced by the store on every write.

1.4 — The Graph Is a Query, Not a Structure

Relationships between events (links, threads, groups, traces) are expressed as annotations on event nodes, not as separate edge entities. A "thread" is not a database table — it is a query: filter: { annotations.thread.thread_id == 'X' }. A "group" is not a collection object — it is a query: filter: { annotations.groups contains 'Y' }. The graph structure emerges from annotations and is traversed via queries.

1.5 — All Annotations Are Queryable Without Decryption

Annotations are stored in plaintext in the annotation envelope. They are never encrypted (by definition — they exist to be queryable by the store without payload decryption). Sensitive information must not be placed in annotations. Annotations describe relationships and structure; content belongs in the encrypted payload.

1.6 — Array Conflict Resolution

Array annotation fields (groups, links, threads, external_refs, recipient.labels, ai.entities, ai.topics) require defined conflict semantics when two annotators write concurrently.

Conflict resolution rule: last-writer-wins with a deterministic tiebreaker. The ordering key for any array annotation write is the tuple (annotated_at_ms, annotating_key_fingerprint), compared lexicographically. When two xp.graph.annotate events arrive with identical millisecond timestamps, the one whose sender key fingerprint sorts later lexicographically wins. Key fingerprints are deterministic fixed values, making this tiebreaker always unambiguous.

Duplicate entry semantics: Set-membership array fields enforce uniqueness by a canonical key. An append introducing a duplicate is silently deduplicated — the higher-ordered entry (by conflict key) is retained. A remove_from targeting a non-existent entry is a no-op, not an error.

Field Uniqueness Key
groups group_id
links (event_id, rel) pair
threads thread_id
external_refs (system, entity, id) triple
recipient.labels label string value
ai.entities entity string value
ai.topics topic string value

Ordering guarantees: Array fields do NOT guarantee insertion order in query results unless an explicit sort is applied. Consumers must not rely on array element position for semantic meaning. For threads, the position field within each thread entry is the authoritative ordering mechanism — not array index position.


2. Annotation Namespaces

The annotation envelope is a structured dictionary organized into namespaces. Each namespace covers a distinct concern.

2.1 — Standard Namespaces

Namespace Purpose Section
thread.* Conversation threading and reply structure §3
recipient.* Per-recipient inbox state (read, folder, star) §4
trace.* Distributed tracing across systems §5
groups Group membership (users, events, data) §6
links Typed relationships to other events §7
workflow.* Process and approval workflow state §8
versioning.* Version chains and supersession §9
ai.* AI-derived semantic tags and summaries §10
custom.* Application-specific extensions §11

2.2 — Reserved vs Extension Namespaces

thread, recipient, trace, groups, links, workflow, versioning, and ai are reserved standard namespaces defined by this specification. Their schemas are protocol-standard.

custom is the extension namespace. Applications declare their own annotation schemas under custom.* without conflicting with the standard namespaces.

Vendors may also declare named top-level namespaces under their own domain: salesforce.annotations.*, stripe.annotations.*. These follow the same DNS-ownership rules as event kind namespaces.


3. Thread Namespace — Conversation Structure

3.1 — Purpose

Links events into threaded conversations without a separate thread entity. Any sequence of events can be organized into a thread after the fact. Events can belong to multiple threads simultaneously.

3.2 — Canonical Representation

The thread namespace always uses the plural array form threads, even when an event belongs to only one thread. The singular thread.* dot-notation is used when referring to field names within an entry, but the top-level annotation key is always threads (an array). Consumers must never assume the singular form. This eliminates the ambiguity of handling both a scalar and an array representation.

"annotations": {
  "threads": [
    {
      "thread_id":         "thread-support-case-123",
      "root_event_id":     "sha256-first-message",
      "reply_to_event_id": null,
      "depth":             0,
      "position":          1,
      "subject":           "Billing issue Q2",
      "closed":            false
    },
    {
      "thread_id":         "thread-internal-review",
      "root_event_id":     "sha256-review-start",
      "reply_to_event_id": "sha256-manager-comment",
      "depth":             2,
      "position":          7,
      "subject":           null,
      "closed":            false
    }
  ]
}

A single-thread event uses the same structure with a one-element array. No special-casing. No singular form ever.

3.3 — Fields (per thread entry)

Field Type Auth Mutable Indexed Description
thread_id string (UUID) PARTICIPANTS No Yes Identifies the thread this entry belongs to
root_event_id string (SHA-256) PARTICIPANTS No Yes The first event in this thread
reply_to_event_id string (SHA-256) SENDER_ONLY No Yes Direct parent event; null for root events
depth integer PARTICIPANTS No Yes Nesting depth (0 = root, 1 = direct reply, etc.)
position integer PARTICIPANTS No Yes Sequential position within the thread; authoritative sort key
subject string PARTICIPANTS Yes Yes Human-readable thread title; null if untitled
closed boolean PARTICIPANTS Yes Yes Whether new replies are permitted

3.4 — Query Patterns

// Retrieve all events in a thread, ordered by position
{
  "kind": "xp.store.query",
  "payload": {
    "filter": { "field": "annotations.threads[*].thread_id", "op": "eq",
                "value": "thread-abc123" },
    "sort":   [{ "field": "annotations.threads[thread_id=thread-abc123].position",
                 "direction": "asc" }]
  }
}

// Retrieve direct replies to a specific event
{
  "kind": "xp.store.query",
  "payload": {
    "filter": { "field": "annotations.threads[*].reply_to_event_id", "op": "eq",
                "value": "sha256-parent-event" }
  }
}

// Retrieve all root-level events (depth 0) in a thread
{
  "kind": "xp.store.query",
  "payload": {
    "filter": {
      "and": [
        { "field": "annotations.threads[*].thread_id", "op": "eq", "value": "thread-abc123" },
        { "field": "annotations.threads[*].depth", "op": "eq", "value": 0 }
      ]
    }
  }
}

3.5 — Conflict Semantics for Threads

threads is an array field with uniqueness key thread_id. Two participants may independently add the same event to the same thread (e.g., both annotating thread_id: "thread-abc123"). The store deduplicates by thread_id, retaining the entry from the higher-ordered (annotated_at_ms, annotating_key_fingerprint) pair. position values are assigned by the annotating participant and are not globally coordinated — in the rare case of a position collision, query results are sorted stably by (position, annotated_at_ms).


4. Recipient Namespace — Personal Inbox State

4.1 — Purpose

Organizes events from the recipient's personal perspective — read state, folders, stars, snooze — independently of how the sender organized them. All recipient annotations are RECIPIENT_ONLY. Different recipients of the same event have completely independent recipient views.

4.2 — Fields

Field Type Auth Mutable Indexed Description
recipient.read boolean RECIPIENT_ONLY Yes Yes Whether the recipient has read this event
recipient.read_at integer (unix ms) RECIPIENT_ONLY Yes No When it was first read
recipient.starred boolean RECIPIENT_ONLY Yes Yes Starred/flagged for follow-up
recipient.archived boolean RECIPIENT_ONLY Yes Yes Moved to archive
recipient.folder string RECIPIENT_ONLY Yes Yes User-defined folder name
recipient.labels array RECIPIENT_ONLY Yes Yes Personal labels applied by recipient
recipient.snoozed_until integer (unix ms) RECIPIENT_ONLY Yes Yes Snooze expiry; null if not snoozed
recipient.priority enum RECIPIENT_ONLY Yes Yes urgent, high, normal, low
recipient.note string RECIPIENT_ONLY Yes No Private note on this event (not shared)

4.3 — Query Patterns

// All unread events addressed to me
{
  "kind": "xp.store.query",
  "payload": {
    "filter": {
      "and": [
        { "field": "recipient", "op": "eq", "value": "<my_public_key>" },
        { "field": "annotations.recipient.read", "op": "eq", "value": false }
      ]
    },
    "sort": [{ "field": "timestamp", "direction": "desc" }]
  }
}

// All starred events in the "client-acme" folder
{
  "kind": "xp.store.query",
  "payload": {
    "filter": {
      "and": [
        { "field": "annotations.recipient.starred", "op": "eq", "value": true },
        { "field": "annotations.recipient.folder", "op": "eq", "value": "client-acme" }
      ]
    }
  }
}

5. Trace Namespace — Distributed Observability

5.1 — Purpose

Connects events across multiple services and systems into a complete, cryptographically-verifiable trace of a logical operation. Eliminates the need for separate tracing infrastructure (Jaeger, Zipkin, OpenTelemetry collectors) for XProtocol-native systems.

Because every annotation is a signed event, the trace record cannot be retroactively tampered with. Each system's contribution to a trace is signed by that system's key.

5.2 — ID Taxonomy

The trace namespace uses multiple distinct identifiers, each answering a different question about causality and context:

ID Field Type Scope Question It Answers
trace.trace_id string (UUID) All systems in one logical operation What is the full end-to-end story?
trace.span_id string (short UUID) This event's contribution What did this system do?
trace.parent_span_id string (short UUID) The span that caused this one What triggered this system to act?
trace.causation_id string (SHA-256) The specific event that caused this Which exact event (by content hash) triggered this?
trace.correlation_id string (UUID) User-visible operation reference What is the human-readable reference number?
trace.saga_id string (UUID) Multi-step transaction Which transaction is this event part of?
trace.session_id string (UUID) User authentication session Which user session originated this?
trace.batch_id string (UUID) Batch operation Which batch job is this event part of?
trace.job_id string (UUID) Scheduled job Which cron or scheduled job triggered this?
trace.request_id string (UUID) Inbound gateway request What was the originating API gateway request?

5.3 — Fields

Field Type Auth Mutable Indexed Description
trace.trace_id string SENDER_ONLY No Yes Top-level operation identifier; same across all systems
trace.span_id string SENDER_ONLY No Yes This event's span within the trace
trace.parent_span_id string SENDER_ONLY No Yes Parent span; null for root events
trace.causation_id string (SHA-256) SENDER_ONLY No Yes SHA-256 ID of the event that caused this one
trace.correlation_id string SENDER_ONLY No Yes User-visible request reference
trace.saga_id string SENDER_ONLY No Yes Transaction/saga identifier
trace.session_id string SENDER_ONLY No Yes Originating user session
trace.batch_id string SENDER_ONLY No Yes Batch operation identifier
trace.job_id string SENDER_ONLY No Yes Scheduled job identifier
trace.request_id string SENDER_ONLY No Yes Inbound API gateway request ID

All trace fields are SENDER_ONLY and immutable — they are set once by the sending system and cannot be altered. This prevents post-hoc modification of trace chains.

5.4 — Query Patterns

// Full trace reconstruction — every event in a logical operation
{
  "kind": "xp.store.query",
  "payload": {
    "filter": { "field": "annotations.trace.trace_id", "op": "eq", "value": "trace-7f3a9c" },
    "sort": [{ "field": "timestamp", "direction": "asc" }]
  }
}

// All events caused by a specific event (direct children in trace tree)
{
  "kind": "xp.store.query",
  "payload": {
    "filter": { "field": "annotations.trace.causation_id", "op": "eq",
                "value": "sha256-parent-event-hash" }
  }
}

// Everything in a failed saga for incident response
{
  "kind": "xp.store.query",
  "payload": {
    "filter": { "field": "annotations.trace.saga_id", "op": "eq", "value": "saga-def456" },
    "sort": [{ "field": "timestamp", "direction": "asc" }]
  }
}

// All events from a user session across all services
{
  "kind": "xp.store.query",
  "payload": {
    "filter": { "field": "annotations.trace.session_id", "op": "eq", "value": "sess-ghi789" }
  }
}

5.5 — Trace Visualization: xp.graph.trace.view

The standard query for trace visualization. Returns a pre-computed DAG and waterfall dataset ready for rendering by any client — equivalent to Jaeger or Zipkin's trace view, built entirely from XProtocol events with no separate collector infrastructure.

{
  "kind": "xp.graph.trace.view",
  "payload": {
    "trace_id": "trace-7f3a9c",
    "layout":   "waterfall"
  }
}

layout values:

Value Description
waterfall Gantt-style timeline; each span is a horizontal bar. Standard Jaeger/Zipkin view.
dag Directed acyclic graph; nodes are events, edges are parent_span_id relationships.
tree Hierarchical tree; root span at top, children indented by depth.

Response:

{
  "kind": "xp.graph.trace.view.result",
  "payload": {
    "trace_id":    "trace-7f3a9c",
    "layout":      "waterfall",
    "root_span_id": "span-1a0e",
    "duration_ms": 342,
    "span_count":  7,
    "nodes": [
      {
        "span_id":        "span-1a0e",
        "parent_span_id": null,
        "event_id":       "sha256-abc123",
        "event_kind":     "xp.message.direct",
        "sender_fingerprint": "a1b2:c3d4:...",
        "started_at_ms":  1717000000000,
        "duration_ms":    342,
        "depth":          0,
        "status":         "ok",
        "causation_id":   null
      },
      {
        "span_id":        "span-2b1f",
        "parent_span_id": "span-1a0e",
        "event_id":       "sha256-def456",
        "event_kind":     "salesforce.opportunity.create",
        "sender_fingerprint": "e5f6:7890:...",
        "started_at_ms":  1717000000045,
        "duration_ms":    198,
        "depth":          1,
        "status":         "ok",
        "causation_id":   "sha256-abc123"
      }
    ],
    "edges": [
      { "from": "span-1a0e", "to": "span-2b1f", "rel": "caused_by" }
    ],
    "errors": [],
    "truncated": false
  }
}

status values per node: ok, error, timeout, cancelled. The store determines status by checking whether the event's correlation chain includes an xp.error response event.

duration_ms calculation: From this event's timestamp to the latest timestamp of any event in its subtree (all descendants via parent_span_id). For leaf spans (no children), duration is the difference between this event's timestamp and any acknowledged response event's timestamp (via correlation_id), or zero if no response is found.

errors array: A list of { span_id, event_kind, error_code } entries for any span whose correlation chain contains an xp.error event. Allows renderers to highlight the failure point in the waterfall.

The xp.graph.trace.view operation is declared optional in capability announcements — basic Graph Stores may not implement it. Clients that need visualization may fall back to composing it from xp.store.query results filtered by trace_id.


6. Groups — Universal Membership

6.1 — Purpose

Groups provide a universal membership primitive that applies to any entity type simultaneously: events, users (keys), data records, services. A group is not a database table or a collection object — it is a label that makes its members discoverable via a common query.

6.2 — Group Definition

Groups are defined by sending an xp.graph.group.define event:

{
  "kind": "xp.graph.group.define",
  "payload": {
    "group_id": "project-phoenix",
    "group_type": "project",
    "display_name": "Project Phoenix",
    "description": "Q2 2026 platform rewrite initiative",
    "owner_key": "<alice_public_key>",
    "authorized_members": [
      "<alice_public_key>",
      "<bob_public_key>",
      "<carol_public_key>"
    ],
    "authorized_annotators": ["PARTICIPANTS"],
    "visibility": "members_only"
  }
}

The group definition event is itself stored in the Graph Store, queryable like any other event.

6.3 — Membership Annotation

Any event, user key metadata event, or data event can declare membership by adding a groups annotation:

"annotations": {
  "groups": [
    { "group_id": "project-phoenix", "group_type": "project",  "added_at": 1717000000 },
    { "group_id": "client-acme",     "group_type": "client",   "added_at": 1717000000 },
    { "group_id": "q2-2026",         "group_type": "period",   "added_at": 1717000000 },
    { "group_id": "high-value",      "group_type": "classification" }
  ]
}

6.4 — Group Types (Standard)

Type Description
project A named initiative or project
client A customer or client organization
team An internal team or department
period A time period (sprint, quarter, fiscal year)
classification A data classification level
workflow A workflow instance
campaign A marketing or outreach campaign
incident An incident response group
custom Application-defined group type

Custom group types are defined by the application under custom.* in the group type field.

6.5 — Query Patterns

// Every event, user, and data record in project-phoenix
{
  "kind": "xp.store.query",
  "payload": {
    "filter": {
      "field": "annotations.groups[*].group_id",
      "op": "contains",
      "value": "project-phoenix"
    }
  }
}

// Count events per group for a dashboard
{
  "kind": "xp.store.stats",
  "payload": {
    "group_by": "annotations.groups[*].group_id",
    "filter": { "field": "timestamp", "op": "gte", "value": 1717000000 }
  }
}

7.1 — Purpose

Links express typed, directed relationships between events without requiring a separate edge entity. The graph of events is traversable via the link annotations.

"annotations": {
  "links": [
    {
      "event_id": "sha256-def456",
      "rel": "caused_by",
      "direction": "outbound",
      "added_at": 1717000000,
      "added_by": "<alice_public_key_fingerprint>"
    },
    {
      "event_id": "sha256-ghi789",
      "rel": "superseded_by",
      "direction": "outbound"
    },
    {
      "event_id": "sha256-jkl012",
      "rel": "related_to",
      "direction": "bidirectional"
    }
  ]
}

7.3 — Standard Relationship Types

rel value Semantics Direction
caused_by This event was caused by the linked event Outbound
supersedes This event replaces the linked event Outbound
superseded_by This event has been replaced by the linked event Outbound
reply_to This event is a reply to the linked event Outbound
references This event references the linked event Outbound
part_of This event is a component of the linked event (saga, batch) Outbound
related_to General association; no causal implication Bidirectional
blocks This event represents a blocking dependency Outbound
blocked_by This event is blocked by the linked event Outbound
duplicates This event is a duplicate of the linked event Bidirectional
custom.* Application-defined relationship type Declared by schema

7.4 — Graph Traversal Query

// Fetch an event and all events it links to (one hop)
{
  "kind": "xp.graph.traverse",
  "payload": {
    "start_event_id": "sha256-root",
    "direction": "outbound",
    "rel_filter": ["caused_by", "part_of"],
    "max_depth": 3,
    "include_annotations": true
  }
}

The xp.graph.traverse query is the Graph Store extension to xp.store.query — it follows link edges rather than filtering by field. The result is a subgraph: a set of events connected by the traversed links, returned with their full annotation envelopes.

7.5 — External System References

A special case of links: references to entities in external systems that do not use XProtocol. These carry no graph traversal semantics (there is no XProtocol event to traverse to) but provide a universal foreign key system across all integrated services.

"annotations": {
  "external_refs": [
    { "system": "salesforce",  "entity": "opportunity", "id": "opp-789" },
    { "system": "jira",        "entity": "issue",       "id": "PROJ-4421" },
    { "system": "stripe",      "entity": "invoice",     "id": "inv_abc123" },
    { "system": "github",      "entity": "pull_request", "id": "1847" },
    { "system": "zendesk",     "entity": "ticket",      "id": "TKT-8823" }
  ]
}

Query by external reference:

{
  "kind": "xp.store.query",
  "payload": {
    "filter": {
      "and": [
        { "field": "annotations.external_refs[*].system", "op": "eq", "value": "jira" },
        { "field": "annotations.external_refs[*].id", "op": "eq", "value": "PROJ-4421" }
      ]
    }
  }
}

8. Workflow Namespace — Process State

8.1 — Purpose

Tracks the state of any event as it moves through a business process. Any event can be a workflow artifact. The workflow state is the annotation — not a separate workflow database entity. No workflow engine required. The workflow transitions are themselves signed annotation events, providing a complete, tamper-evident process history.

8.2 — Fields

Field Type Auth Mutable Indexed Description
workflow.workflow_id string PARTICIPANTS No Yes Which workflow definition this follows
workflow.instance_id string (UUID) PARTICIPANTS No Yes This specific workflow instance
workflow.current_step string AUTHORIZED_KEYS Yes Yes Current step name
workflow.status enum AUTHORIZED_KEYS Yes Yes active, completed, cancelled, failed
workflow.assigned_to string (key) AUTHORIZED_KEYS Yes Yes Key currently responsible
workflow.steps_completed array AUTHORIZED_KEYS Yes No Ordered list of completed step names
workflow.steps_remaining array AUTHORIZED_KEYS Yes No Ordered list of remaining step names
workflow.due_at integer (unix ms) AUTHORIZED_KEYS Yes Yes Deadline for current step
workflow.started_at integer (unix ms) STORE_MANAGED No Yes When workflow was initiated
workflow.completed_at integer (unix ms) STORE_MANAGED No Yes When workflow reached terminal state

8.3 — Example

"annotations": {
  "workflow": {
    "workflow_id":        "contract-approval-v3",
    "instance_id":        "wf-inst-7a3b2c",
    "current_step":       "legal-review",
    "status":             "active",
    "assigned_to":        "<legal_team_key_fingerprint>",
    "steps_completed":    ["submitted", "manager-approved"],
    "steps_remaining":    ["legal-review", "finance-sign-off", "countersign"],
    "due_at":             1717172800
  }
}

8.4 — Query Patterns

// All events currently in legal-review step, ordered by due date
{
  "kind": "xp.store.query",
  "payload": {
    "filter": {
      "and": [
        { "field": "annotations.workflow.current_step", "op": "eq", "value": "legal-review" },
        { "field": "annotations.workflow.status", "op": "eq", "value": "active" }
      ]
    },
    "sort": [{ "field": "annotations.workflow.due_at", "direction": "asc" }]
  }
}

// Overdue workflow items (due_at in the past, still active)
{
  "kind": "xp.store.query",
  "payload": {
    "filter": {
      "and": [
        { "field": "annotations.workflow.status", "op": "eq", "value": "active" },
        { "field": "annotations.workflow.due_at", "op": "lt", "value": 1717000000 }
      ]
    }
  }
}

9. Versioning Namespace — Immutable Version Chains

9.1 — Purpose

Provides logical versioning for events that represent documents, records, or configurations that evolve over time. Because events are immutable, "updating" a record means publishing a new event and annotating it as a new version of the original. The version chain is navigable and the full history is always queryable.

9.2 — Fields

Field Type Auth Mutable Indexed Description
versioning.version integer SENDER_ONLY No Yes Version number (1-based)
versioning.entity_id string (UUID) SENDER_ONLY No Yes Stable identifier across all versions of this entity
versioning.supersedes array SENDER_ONLY No Yes SHA-256 IDs of prior versions this supersedes
versioning.superseded_by string (SHA-256) STORE_MANAGED Yes Yes Set by store when a newer version is stored; null = current
versioning.is_current boolean STORE_MANAGED Yes Yes True only for the latest non-deleted version
versioning.status enum AUTHORIZED_KEYS Yes Yes active, deprecated, superseded, deleted
versioning.changelog string SENDER_ONLY No No Human-readable description of changes from prior version
versioning.major boolean SENDER_ONLY No Yes Whether this is a breaking change from the prior version

9.3 — Status Lifecycle

versioning.status tracks the operational state of a versioned record independently of the cryptographic supersession chain:

Status Meaning is_current
active Current, valid, in use Yes (if latest version)
deprecated Still valid but successor preferred; consumers should migrate Yes (if latest version)
superseded Replaced by a newer version; do not use No
deleted Soft-deleted; treat as non-existent for application purposes No

Soft delete semantics: The deleted status marks an event as logically removed without physically deleting it from the store. Physical deletion is not possible without breaking the event's signature and the store's audit integrity. For GDPR right-to-erasure compliance, deleted status combined with an xp.store.retention payload-revocation event achieves the practical effect: the record is unfindable in normal queries and the payload is cryptographically inaccessible, while the event envelope (which contains no personal data) remains for audit chain integrity.

Applications that query versioned entities should include a default filter of versioning.status NOT IN ('superseded', 'deleted') unless they explicitly need historical versions.

9.4 — Query Patterns

// Current active version of an entity
{
  "kind": "xp.store.query",
  "payload": {
    "filter": {
      "and": [
        { "field": "annotations.versioning.entity_id", "op": "eq", "value": "entity-uuid-abc" },
        { "field": "annotations.versioning.is_current", "op": "eq", "value": true },
        { "field": "annotations.versioning.status", "op": "eq", "value": "active" }
      ]
    }
  }
}

// Full version history of an entity (including deleted)
{
  "kind": "xp.store.query",
  "payload": {
    "filter": { "field": "annotations.versioning.entity_id", "op": "eq",
                "value": "entity-uuid-abc" },
    "sort": [{ "field": "annotations.versioning.version", "direction": "asc" }]
  }
}

// All deprecated records that need migration
{
  "kind": "xp.store.query",
  "payload": {
    "filter": { "field": "annotations.versioning.status", "op": "eq",
                "value": "deprecated" }
  }
}

10. AI Namespace — Semantic Tags

10.1 — Purpose

Enables AI agents to annotate events with semantic metadata derived from decrypting and processing the payload locally — without the store ever seeing the content. The resulting semantic tags are stored as unencrypted annotations, making them queryable without re-processing.

This creates a persistent semantic index — AI-derived labels that make encrypted content discoverable by meaning rather than just by metadata.

10.2 — Fields

Field Type Auth Mutable Indexed Description
ai.intent string AUTHORIZED_KEYS Yes Yes Detected intent (e.g., purchase-inquiry, support-request)
ai.sentiment enum AUTHORIZED_KEYS Yes Yes positive, neutral, negative, mixed
ai.entities array AUTHORIZED_KEYS Yes Yes Named entities extracted from content
ai.topics array AUTHORIZED_KEYS Yes Yes Topic labels
ai.language string (BCP-47) AUTHORIZED_KEYS Yes Yes Detected language code
ai.summary string AUTHORIZED_KEYS Yes No Short human-readable summary (not stored in index)
ai.embedding_ref string (SHA-256) AUTHORIZED_KEYS Yes No Reference to a separately stored embedding vector blob
ai.model string AUTHORIZED_KEYS Yes No Model identifier that produced these tags
ai.confidence float AUTHORIZED_KEYS Yes No Confidence score for the annotations (0.0–1.0)
ai.processed_at integer (unix ms) AUTHORIZED_KEYS Yes No When the AI processing occurred

10.3 — Security Note and AI Annotator Safety Profile

AI annotations are produced by decrypting the payload locally on an authorized device and then annotating the store with derived semantic tags. The store never receives the decrypted payload. Only the derived tags — which must not contain sensitive verbatim content — are stored as annotations.

The categorical vs extractive rule: This is the core safety constraint for AI annotators. Every ai.* field must be categorical (a label from a defined or bounded vocabulary) rather than extractive (a substring, paraphrase, or reconstruction of source content). The distinction:

Safe — Categorical Unsafe — Extractive
ai.intent: "purchase-inquiry" ai.summary: "Alice wants 500 units of Product X by Friday"
ai.sentiment: "positive" ai.entities: ["Alice Johnson", "500", "Product X"]
ai.topics: ["pricing", "delivery"] ai.summary: "Customer inquiring about bulk discount on Q2 order"
ai.entities: ["acme-corp"] ai.entities: ["alice@acme-corp.com"]

The rule in one sentence: if removing the annotation would allow a reader to reconstruct any specific fact from the payload, the annotation is extractive and must not be stored.

Recommended AI Annotator Safety Profile:

Implementations that produce ai.* annotations should enforce the following constraints before writing:

  1. No proper nouns from source content in ai.entities unless they are organization names that appear in a known entity registry (i.e., the entity was recognized, not extracted verbatim).
  2. No numeric values from source content (prices, quantities, dates, identifiers, phone numbers) in any ai.* field.
  3. No substrings longer than 32 characters from source content in any ai.* field.
  4. ai.summary must be abstractive, not extractive. It must be a re-expression in different words, not a selection of phrases from the source. If the annotator cannot produce a fully abstractive summary, ai.summary must be omitted.
  5. ai.intent and ai.topics must draw from a declared controlled vocabulary defined in the annotation schema. Open-ended string values for these fields are discouraged; enum types are preferred.
  6. ai.entities values must be normalized (lowercase, no punctuation, canonical form) to prevent fingerprinting through capitalization or punctuation patterns that match source content.

These constraints are recommendations, not protocol enforcement — the store cannot verify them because it never sees the payload. Annotator implementations are responsible for applying them.

ai.embedding_ref special case: An embedding vector stored as a separately encrypted blob (referenced by SHA-256 hash) may encode semantic information in a form that resists extraction. Embedding blobs should be encrypted to the same key set as the source event. The ai.embedding_ref field stores only the content hash — never the vector itself in plaintext.

10.4 — Query Patterns

// All events with purchase-inquiry intent from the last 30 days
{
  "kind": "xp.store.query",
  "payload": {
    "filter": {
      "and": [
        { "field": "annotations.ai.intent", "op": "eq", "value": "purchase-inquiry" },
        { "field": "timestamp", "op": "gte", "value": 1714408000 }
      ]
    }
  }
}

// Events mentioning a specific entity
{
  "kind": "xp.store.query",
  "payload": {
    "filter": {
      "field": "annotations.ai.entities",
      "op": "contains",
      "value": "acme-corp"
    }
  }
}

11. Custom Namespace — Application Extensions

11.1 — Purpose

Applications declare their own annotation fields under the custom namespace without conflicting with the standard namespaces. Custom annotations follow the same authorization, mutability, and indexing model as standard annotations.

11.2 — Declaration

Custom annotations are declared in an Annotation Schema event (§12) before use:

{
  "kind": "xp.graph.annotation-schema.define",
  "payload": {
    "namespace": "custom.crm",
    "applies_to": ["salesforce.opportunity.*", "acme.order.*"],
    "annotations": {
      "custom.crm.opportunity_id": {
        "type": "string",
        "auth": "SENDER_ONLY",
        "mutable": false,
        "indexed": true,
        "description": "Salesforce Opportunity ID linked to this event"
      },
      "custom.crm.deal_stage": {
        "type": "enum",
        "values": ["prospecting", "qualified", "proposal", "negotiation", "closed"],
        "auth": "AUTHORIZED_KEYS",
        "mutable": true,
        "indexed": true
      }
    }
  }
}

12. Annotation Schema System

12.1 — Purpose

Annotation schemas are machine-readable contracts that define which annotation fields are valid for a given event kind, who can write them, whether they are mutable, and whether they are indexed. The store enforces annotation schemas on every xp.graph.annotate write.

12.2 — Schema Definition Event

{
  "kind": "xp.graph.annotation-schema.define",
  "payload": {
    "schema_id": "xp.message.direct.annotations.v1",
    "applies_to": "xp.message.direct",
    "annotations": {
      "thread.thread_id": {
        "type": "string",
        "auth": "PARTICIPANTS",
        "mutable": false,
        "indexed": true
      },
      "recipient.read": {
        "type": "boolean",
        "auth": "RECIPIENT_ONLY",
        "mutable": true,
        "indexed": true
      },
      "recipient.folder": {
        "type": "string",
        "auth": "RECIPIENT_ONLY",
        "mutable": true,
        "indexed": true
      },
      "trace.trace_id": {
        "type": "string",
        "auth": "SENDER_ONLY",
        "mutable": false,
        "indexed": true
      },
      "groups": {
        "type": "array<group_ref>",
        "auth": "PARTICIPANTS",
        "mutable": true,
        "indexed": true
      },
      "links": {
        "type": "array<link_ref>",
        "auth": "PARTICIPANTS",
        "mutable": true,
        "indexed": true
      },
      "ai.intent": {
        "type": "string",
        "auth": "AUTHORIZED_KEYS",
        "mutable": true,
        "indexed": true
      }
    }
  }
}

12.3 — Authorization Levels

Level Who Can Write
SENDER_ONLY Only the original event sender's key
RECIPIENT_ONLY Only the original event recipient's key
PARTICIPANTS Either the sender or any recipient
AUTHORIZED_KEYS Keys explicitly granted annotation authority for this field
STORE_MANAGED Only the store itself (e.g., delivery status, superseded_by)
ANY_VERIFIED Any key with a verified signature (public annotations)

12.4 — Schema Inheritance

Annotation schemas support inheritance. A schema for xp.message.* declares common fields that apply to all message subtypes. A schema for xp.message.direct inherits those fields and may add its own:

{
  "kind": "xp.graph.annotation-schema.define",
  "payload": {
    "schema_id": "xp.message.annotations.v1",
    "applies_to": "xp.message.*",
    "inherits": [],
    "annotations": { ... }
  }
}

13. The xp.graph.* Schema Family

All Graph Store operations use the xp.graph.* namespace. The xp.store.* namespace handles base event storage and retrieval; the xp.graph.* namespace handles annotation management and graph traversal.

13.1 — Schema Inventory

xp.graph.annotate                    — add or update annotations on a stored event
xp.graph.annotate.bulk               — annotate multiple events in one operation
xp.graph.annotation.history         — retrieve the full annotation change history for an event
xp.graph.annotation-schema.define   — declare the annotation schema for an event kind
xp.graph.annotation-schema.get      — retrieve an annotation schema
xp.graph.traverse                   — traverse the event graph by following link edges
xp.graph.trace.view                  — return pre-computed DAG/waterfall data for a trace_id
xp.graph.group.define               — create a new named group
xp.graph.group.get                  — retrieve a group definition
xp.graph.group.list                 — list all groups accessible to the requesting key
xp.graph.group.membership.add       — add an entity to a group
xp.graph.group.membership.remove    — remove an entity from a group

13.2 — xp.graph.annotate

The primary write operation. Adds or updates annotation fields on a stored event.

{
  "kind": "xp.graph.annotate",
  "payload": {
    "target_event_id": "sha256-abc123",
    "operation": "merge",
    "annotations": {
      "thread.thread_id": "thread-xyz789",
      "thread.depth": 0,
      "thread.position": 1,
      "groups": [
        { "group_id": "project-phoenix", "group_type": "project" }
      ],
      "recipient.read": false,
      "trace.trace_id": "trace-7f3a9c",
      "trace.span_id": "span-2b1f"
    }
  }
}

Operations:

Operation Semantics
set Set a specific annotation key to a value (replaces existing)
merge Merge a dictionary of annotations (each key replaces its prior value)
append Append to an array annotation (groups, links, external_refs)
remove Remove a specific key from the annotation envelope
remove_from Remove a specific item from an array annotation

The store validates: 1. The annotating key's signature 2. The annotating key's authorization level for each field being set 3. Compliance with the declared annotation schema for this event kind 4. Immutability constraints (cannot set a field marked mutable: false twice)

13.3 — xp.graph.traverse

Graph traversal query — follows link edges rather than filtering by field.

{
  "kind": "xp.graph.traverse",
  "payload": {
    "start_event_id": "sha256-root",
    "direction": "outbound",
    "rel_filter": ["caused_by", "part_of", "reply_to"],
    "max_depth": 5,
    "include_annotations": true,
    "page_size": 50
  }
}

Response:

{
  "kind": "xp.graph.traverse.result",
  "payload": {
    "nodes": [
      {
        "event": "<XpEvent encrypted>",
        "annotations": { ... },
        "depth": 0,
        "incoming_rel": null
      },
      {
        "event": "<XpEvent encrypted>",
        "annotations": { ... },
        "depth": 1,
        "incoming_rel": "caused_by"
      }
    ],
    "edges": [
      { "from": "sha256-root", "to": "sha256-child", "rel": "caused_by" }
    ],
    "truncated": false
  }
}

13.4 — xp.graph.annotation.history

Every annotation change is a signed event stored in the Graph Store. This operation retrieves the full mutation history for a specific annotation field on a specific event.

{
  "kind": "xp.graph.annotation.history",
  "payload": {
    "target_event_id": "sha256-abc123",
    "annotation_key": "workflow.current_step"
  }
}

Response includes each mutation: the value before and after, the key that made the change, and the timestamp — all cryptographically verifiable from the stored annotation events.


14. What the Graph Store Replaces

A single XProtocol Graph Store deployment answers queries that currently require assembling and integrating multiple distinct infrastructure components:

Traditional Stack Graph Store Equivalent Security Improvement
NoSQL document store Permanent encrypted event storage + metadata queries Cryptographic access control; provable authorship
Graph database (Neo4j) Typed link annotations + xp.graph.traverse Identity-native; every edge is authorized and auditable
Distributed tracing (Jaeger, Zipkin, OTel) trace.* annotation namespace Tamper-evident; every span is signed by its producing system
Message queue (Kafka, Kinesis) xp.store.subscribe with historical replay End-to-end encrypted; identity-native
Email inbox (IMAP folder model) recipient.* annotation namespace Per-recipient view without shared-state server
Workflow engine (Temporal, Airflow) workflow.* annotation namespace State machine on events; no separate workflow database
Audit log (Splunk, ELK) Event store + annotation history Tamper-evident by construction; every record is signed
Version control for records versioning.* annotation namespace Immutable history; cryptographic proof of change chain
AI memory / semantic search ai.* annotation namespace + embedding refs Persistent semantic index without re-processing
Cross-system foreign keys external_refs in links namespace Universal foreign key across all integrated services
Observability platform trace.* + xp.store.stats No collector infrastructure; cryptographically verifiable

15. Implementation Requirements

15.1 — Required Capabilities

A Graph Store implementation must support:

  1. The xp.store.* schema family (base event store — persistent retention, xp.store.query, xp.store.get, xp.store.subscribe, xp.store.stats)
  2. The xp.graph.annotate operation with all five operations (set, merge, append, remove, remove_from)
  3. Annotation schema enforcement (authorization, mutability, type checking)
  4. Index maintenance for all fields declared indexed: true
  5. xp.graph.annotation.history for any annotated field
  6. Storage of annotation events as first-class events in the store

15.2 — Optional Capabilities

Implementations may additionally support:

  • xp.graph.traverse for link-following graph queries
  • xp.graph.group.* operations
  • AI annotation processing pipeline
  • TEE-based payload-level queries

15.3 — Capability Declaration

Implementations declare their Graph Store capabilities in their xp.endpoint.announce event:

"capabilities": [
  "xp.store.*",
  "xp.graph.annotate",
  "xp.graph.annotation.history",
  "xp.graph.traverse",
  "xp.graph.group.*"
]

16. Security Considerations

16.1 — Annotation Exposure

Annotations are unencrypted by design — they must be queryable by the store without payload decryption. This means annotations must never contain sensitive information. The rule: if you wouldn't want the store operator to see it, it belongs in the encrypted payload, not in an annotation.

16.2 — Annotation Forgery Prevention

Every xp.graph.annotate event is signed by the annotating key. The store validates the signature before applying the annotation. Annotation events are stored alongside the base events — they cannot be silently backdated or forged.

16.3 — AI Annotation Leakage Risk

AI annotations (§10) are derived from decrypted payload content and stored as plaintext. Implementations must ensure that AI-derived tags do not inadvertently reconstruct sensitive payload content through over-specific entity extraction, verbatim phrases in summaries, or highly specific topic labels.

16.4 — Authorization Enforcement

The store must enforce annotation authorization levels (SENDER_ONLY, RECIPIENT_ONLY, PARTICIPANTS, AUTHORIZED_KEYS, STORE_MANAGED) on every write. An authorization failure returns xp.error with code AUTHORIZATION_INSUFFICIENT and does not apply any part of the annotation.


17. Relationship to Other XProtocol Extensions

The Graph Store builds directly on the Event Store (xp.store.*) and the core XProtocol event model. It is used by:

  • XProtocol-Native Environment Model — environment state, capability grants, and deployment authorizations are stored as graph events, queryable via the groups and trace namespaces
  • XProtocol MCP Adapter — AI agents interact with the graph store through MCP tools exposing xp.graph.annotate and xp.graph.traverse
  • XProtocolChat — message threading, inbox state, and conversation history sync are natural graph store applications beginning in V2

18. Schema Governance and Evolution

18.1 — Who Can Propose

Any party may propose a new community annotation schema or modification to an existing one by publishing an xp.graph.annotation-schema.propose event addressed to the community governance endpoint. A proposal must include: the schema definition, a rationale statement, backward compatibility analysis, and at least one reference implementation.

Vendor-owned namespaces (e.g., salesforce.annotations.*) require no community approval — the DNS-owning entity controls them entirely. Only schemas in the xp.* reserved namespace and the org.xp-community.* community namespace require ratification.

18.2 — Ratification Process

The community ratification process follows a lightweight RFC model:

Phase Duration Criterion to Advance
Draft Open-ended Proposal published; at least one implementation exists
Comment 30 days minimum Proposal announced to community; comments collected
Candidate 60 days No blocking objections; two independent implementations pass interop suite
Ratified Permanent Codified in the specification; assigned a version number

A blocking objection is one that identifies a correctness problem, a security vulnerability, or an irreconcilable conflict with an existing ratified schema. Style objections and feature requests do not block ratification.

18.3 — Breaking Changes and Forking

Minor versions add optional fields or new valid enum values. They are non-breaking. Implementations must silently ignore unknown fields per the extensibility rule. Minor versions do not require re-ratification.

Major versions may remove fields, change field types, or change semantics. They are breaking. A major version is a new schema that coexists with prior major versions — they are not replaced, only superseded for new usage. Services declare which major versions they support in their capability announcement.

Forking: If a major vendor diverges from a community schema in an irreconcilable way, both versions coexist. The fork is documented with an xp.graph.annotation-schema.fork event naming the divergence point, the forking party's namespace, and the semantic differences. Neither fork is authoritative over the other; market adoption determines dominance. The protocol accommodates forks — it does not prevent them.

18.4 — Backward Compatibility Promise

Ratified schemas carry the following promises:

  • Minor versions: All implementations supporting version N must accept events annotated with any version N.x without error.
  • Major versions: Implementations must support at minimum the current major version and one prior major version simultaneously.
  • Removal: Fields are never removed from a minor version. Removal requires a major version increment.
  • Deprecation path: A field scheduled for removal must be marked deprecated: true in the schema for at least one full major version before removal.

19. Environment Model Integration

The XProtocol-native environment model (described in XProtocol-Strategic-Concepts.md Concept 2) uses the Graph Store as its state backing. This section specifies the integration points.

19.1 — Environment as a Group

Each logical environment is represented as a named group in the Graph Store:

{
  "kind": "xp.graph.group.define",
  "payload": {
    "group_id":   "env-feature-payments-v2",
    "group_type": "environment",
    "display_name": "feature/payments-v2",
    "owner_key":  "<developer_key>",
    "authorized_members": ["<developer_key>", "<ci_key>"],
    "metadata": {
      "inherits":    "env-dev",
      "data_scope":  "synthetic",
      "expires_at":  1717776000
    }
  }
}

Every event produced within an environment is annotated with the environment group ID:

"annotations": {
  "groups": [{ "group_id": "env-feature-payments-v2", "group_type": "environment" }],
  "trace": { "trace_id": "trace-xyz", "session_id": "sess-abc" }
}

This makes the entire event history of an environment queryable with a single filter, and makes cross-environment queries (e.g., "show me the same operation across dev and prod") trivial.

19.2 — Capability Inheritance: Intersection Semantics

Child environments inherit capabilities from parent environments using set intersection — never union. The effective capability set of a key in a child environment is:

effective_capabilities = parent_capabilities ∩ child_capabilities

A key can never gain capabilities through a child environment that its parent environment does not grant. This is the same model as JWT scope narrowing: you can always restrict inherited capabilities, never expand them.

Example:

parent env (dev):    [read:synthetic, write:dev-schema, deploy:dev]
child override:      [read:synthetic, write:dev-schema]
effective:           [read:synthetic, write:dev-schema]
                     (deploy:dev is tightened out by the child)

19.3 — Capability Discovery

A key operating in an environment can query its own effective capabilities:

{
  "kind": "xp.environment.capabilities.query",
  "payload": {
    "environment_id": "env-feature-payments-v2"
  }
}

Response:

{
  "kind": "xp.environment.capabilities.result",
  "payload": {
    "environment_id":        "env-feature-payments-v2",
    "requesting_key":        "<key_fingerprint>",
    "effective_capabilities": ["read:synthetic", "write:dev-schema"],
    "inherited_from":        "env-dev",
    "expires_at":            1717776000,
    "computed_at":           1717000000
  }
}

The response is signed by the environment management service, not the store — it is an authoritative statement of what the requesting key is permitted to do. Clients cache this with a short TTL (recommended: 60 seconds) and re-query before any capability-sensitive operation.

19.4 — Data Classification via xp.data.classify

Events and records carry their data classification as a standard annotation field:

"annotations": {
  "data_classification": {
    "level":    "synthetic",
    "scope":    "env-feature-payments-v2",
    "set_by":   "<ci_key_fingerprint>",
    "set_at":   1717000000
  }
}

Standard classification levels (ordered by sensitivity):

Level Description Accessible in
synthetic Generated test data; no real user information All environments
internal Real operational data not containing PII Dev, staging, prod
confidential Business-sensitive; restricted access Staging, prod
prod-pii Contains personal identifiable information Prod only
prod-financial Financial records, payment data Prod only with audit

The store enforces classification access at query time: a key in a synthetic-scoped environment cannot retrieve events annotated prod-pii, even if it would otherwise have recipient-based access. Classification enforcement is a mandatory capability of any environment- aware Graph Store implementation.


20. Relay Reputation and High-Trust Relays (Optional Extension)

This section describes an optional extension for relay reputation signaling. It is not part of the core Graph Store specification and is not required for a conformant implementation.

20.1 — Reputation Signals

Relay operators may publish signed reputation signals as XProtocol events, creating a queryable reputation history in the Graph Store:

{
  "kind": "xp.relay.reputation.report",
  "payload": {
    "relay_key":       "<relay_public_key>",
    "reporter_key":    "<reporting_key>",
    "period_start":    1717000000,
    "period_end":      1717086400,
    "events_relayed":  142847,
    "delivery_rate":   0.9994,
    "median_latency_ms": 34,
    "spam_events_rejected": 12,
    "uptime_fraction": 0.9998
  }
}

These reports accumulate in the Graph Store and are queryable by any key evaluating a relay's trustworthiness.

20.2 — Stake-Based High-Trust Designation (Optional)

For ecosystems that want economic commitment from relay operators, an optional staking primitive allows relay operators to post cryptographic proof of stake against a public key. Relays that have posted stake and maintained a high reputation score over a rolling 90-day window may declare themselves high-trust in their xp.endpoint.announce event.

High-trust designation is not enforced by the protocol — it is a self-declaration that clients can verify by querying the relay's reputation history in the Graph Store. The value is client trust, not protocol privilege.

This primitive is intentionally left lightweight. Mandatory staking or economic gatekeeping of relay operation would deter the community relay operators and self-hosted deployments that are essential to the decentralized model. Staking is opt-in, not required.


Appendix A — Full Annotation Field Reference

Namespace.Field Type Auth Mutable Indexed
threads[*].thread_id string PARTICIPANTS No Yes
threads[*].root_event_id string PARTICIPANTS No Yes
threads[*].reply_to_event_id string SENDER_ONLY No Yes
threads[*].depth integer PARTICIPANTS No Yes
threads[*].position integer PARTICIPANTS No Yes
threads[*].subject string PARTICIPANTS Yes Yes
threads[*].closed boolean PARTICIPANTS Yes Yes
recipient.read boolean RECIPIENT_ONLY Yes Yes
recipient.read_at integer RECIPIENT_ONLY Yes No
recipient.starred boolean RECIPIENT_ONLY Yes Yes
recipient.archived boolean RECIPIENT_ONLY Yes Yes
recipient.folder string RECIPIENT_ONLY Yes Yes
recipient.labels array RECIPIENT_ONLY Yes Yes
recipient.snoozed_until integer RECIPIENT_ONLY Yes Yes
recipient.priority enum RECIPIENT_ONLY Yes Yes
recipient.note string RECIPIENT_ONLY Yes No
trace.trace_id string SENDER_ONLY No Yes
trace.span_id string SENDER_ONLY No Yes
trace.parent_span_id string SENDER_ONLY No Yes
trace.causation_id string SENDER_ONLY No Yes
trace.correlation_id string SENDER_ONLY No Yes
trace.saga_id string SENDER_ONLY No Yes
trace.session_id string SENDER_ONLY No Yes
trace.batch_id string SENDER_ONLY No Yes
trace.job_id string SENDER_ONLY No Yes
trace.request_id string SENDER_ONLY No Yes
groups[*].group_id string PARTICIPANTS Yes Yes
groups[*].group_type string PARTICIPANTS Yes Yes
links[*].event_id string PARTICIPANTS Yes Yes
links[*].rel string PARTICIPANTS Yes Yes
links[*].direction string PARTICIPANTS Yes No
external_refs[*].system string PARTICIPANTS Yes Yes
external_refs[*].entity string PARTICIPANTS Yes Yes
external_refs[*].id string PARTICIPANTS Yes Yes
workflow.workflow_id string PARTICIPANTS No Yes
workflow.instance_id string PARTICIPANTS No Yes
workflow.current_step string AUTHORIZED_KEYS Yes Yes
workflow.status enum AUTHORIZED_KEYS Yes Yes
workflow.assigned_to string AUTHORIZED_KEYS Yes Yes
workflow.steps_completed array AUTHORIZED_KEYS Yes No
workflow.steps_remaining array AUTHORIZED_KEYS Yes No
workflow.due_at integer AUTHORIZED_KEYS Yes Yes
workflow.started_at integer STORE_MANAGED No Yes
workflow.completed_at integer STORE_MANAGED No Yes
versioning.version integer SENDER_ONLY No Yes
versioning.entity_id string SENDER_ONLY No Yes
versioning.supersedes array SENDER_ONLY No Yes
versioning.superseded_by string STORE_MANAGED Yes Yes
versioning.is_current boolean STORE_MANAGED Yes Yes
versioning.status enum AUTHORIZED_KEYS Yes Yes
versioning.changelog string SENDER_ONLY No No
versioning.major boolean SENDER_ONLY No Yes
ai.intent string AUTHORIZED_KEYS Yes Yes
ai.sentiment enum AUTHORIZED_KEYS Yes Yes
ai.entities array AUTHORIZED_KEYS Yes Yes
ai.topics array AUTHORIZED_KEYS Yes Yes
ai.language string AUTHORIZED_KEYS Yes Yes
ai.summary string AUTHORIZED_KEYS Yes No
ai.embedding_ref string AUTHORIZED_KEYS Yes No
ai.model string AUTHORIZED_KEYS Yes No
ai.confidence float AUTHORIZED_KEYS Yes No
ai.processed_at integer AUTHORIZED_KEYS Yes No
data_classification.level enum AUTHORIZED_KEYS Yes Yes
data_classification.scope string AUTHORIZED_KEYS Yes Yes
data_classification.set_by string AUTHORIZED_KEYS Yes No
data_classification.set_at integer AUTHORIZED_KEYS Yes No

XProtocol.ai is an independent open protocol project and is not affiliated with, endorsed by, or connected to XProtocol.org or any related entities.