> ## Documentation Index
> Fetch the complete documentation index at: https://docs.intention.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# Indexer

> How committed blocks become queryable data — the streaming path from a full node to REST and WebSocket surfaces.

The chain commits blocks. Applications ask questions like *what were this account's fills last week* and *what does the book look like right now* — questions that a block-shaped record answers badly. The indexer is what converts one into the other.

On most chains the indexer is infrastructure-critical: the chain emits opaque events and third parties reconstruct meaning from them, so what your application believes depends on which indexer it trusts. Here the reconstruction step does not exist. The [kernel](/protocol/architecture/kernel) emits typed, attributed output — every state change already bound to the transaction that caused it — so the indexer reshapes data rather than inferring it.

That changes what the indexer is for. It is a **serving layer**, not a source of truth. Anything it reports can be checked against the chain, and a disagreement is a bug in the indexer rather than an open question.

## The path

<div className="dg" data-dg="indexer-path">
  <div className="dg-c" style={{aspectRatio:"720 / 318"}}>
    <svg className="dg-w" viewBox="0 0 720 318" aria-hidden="true">
      <path className="dg-wire dg--blue dg-soft" d="M 123.00 102.00 L 155.98 75.97" />

      <path className="dg-head dg--blue" d="M 161.00 72.00 L 158.70 79.42 L 153.25 72.51 Z" />

      <path className="dg-wire dg--blue dg-soft" d="M 123.00 102.00 L 156.23 131.73" />

      <path className="dg-head dg--blue" d="M 161.00 136.00 L 153.30 135.01 L 159.16 128.45 Z" />

      <path className="dg-wire dg--sky dg-soft" d="M 349.00 72.00 L 381.98 98.03" />

      <path className="dg-head dg--sky" d="M 387.00 102.00 L 379.25 101.49 L 384.70 94.58 Z" />

      <path className="dg-wire dg--sky dg-soft" d="M 349.00 136.00 L 382.23 106.27" />

      <path className="dg-head dg--sky" d="M 387.00 102.00 L 385.16 109.55 L 379.30 102.99 Z" />

      <path className="dg-wire dg--green" d="M 536.00 102.00 L 551.60 102.00" />

      <path className="dg-head dg--green" d="M 558.00 102.00 L 551.60 106.40 L 551.60 97.60 Z" />
    </svg>

    <div className="dg-band" style={{left:"20.8333%",top:"8.1761%",width:"29.1667%",height:"47.7987%"}}><span className="dg-cap">split by age</span></div>
    <div className="dg-b dg--blue" style={{left:"0.0000%",top:"19.4969%",width:"16.3889%",height:"25.1572%"}}><span className="dg-t">Full node</span><span className="dg-s">committed blocks, streamed as typed records</span></div>
    <div className="dg-b dg--sky" style={{left:"23.0556%",top:"14.4654%",width:"24.7222%",height:"16.3522%"}}><span className="dg-t">Cache</span><span className="dg-s">recent — from memory</span></div>
    <div className="dg-b dg--sky" style={{left:"23.0556%",top:"34.5912%",width:"24.7222%",height:"16.3522%"}}><span className="dg-t">File store</span><span className="dg-s">historical — durable</span></div>
    <div className="dg-b dg--sky" style={{left:"54.4444%",top:"19.4969%",width:"19.4444%",height:"25.1572%"}}><span className="dg-t">Data service</span><span className="dg-s">presents both as one stream</span></div>
    <div className="dg-b dg--green" style={{left:"78.0556%",top:"19.4969%",width:"21.9444%",height:"25.1572%"}}><span className="dg-t">Gateway → REST · WebSocket</span><span className="dg-s">access, quotas, routing</span></div>
    <div className="dg-b dg-dashed dg-left" style={{left:"0.0000%",top:"67.2956%",width:"48.8889%",height:"27.6730%"}}><span className="dg-t">Why the fork exists</span><span className="dg-s">Head-following is latency-sensitive and small; historical queries are throughput-sensitive and large. One backfill down a shared path would stall the live one.</span></div>
    <div className="dg-b dg-dashed dg-left" style={{left:"51.1111%",top:"67.2956%",width:"48.8889%",height:"27.6730%"}}><span className="dg-t">A serving layer, not a source of truth</span><span className="dg-s">The kernel emits typed, attributed output, so the indexer reshapes rather than infers. A disagreement with the chain is a bug in the indexer.</span></div>
  </div>
</div>

**A full node** is the origin. Trading records are streamed out of it as typed data rather than raw transactions requiring interpretation.

**Cache and file store** split the stream by age. Recent data is served from memory, because that is what most consumers want and latency matters. Historical data is written to durable file storage, because retaining everything in memory is not a strategy. A consumer asking for something old and a consumer following the head are served from different places without either noticing.

**The data service** presents both as one stream. A client asks for a range starting anywhere; whether that range is served from cache, from files, or from both is not the client's problem.

**The gateway** handles the concerns that belong between a service and the public internet: access, quotas, and routing.

**REST and WebSocket surfaces** are what applications actually use — market data, order and fill history, positions, account state, funding payments, and live subscriptions. See the [API reference](https://testnet-openapi.intention.xyz/) for the endpoint-level detail.

## Why the split exists

A single service that both followed the chain head and answered historical queries would do neither well. Head-following is latency-sensitive and small; historical queries are throughput-sensitive and large, and one large backfill would stall the live path.

Separating them means backfilling a new consumer from months ago does not degrade the feed of a market maker following the head, and the two can be scaled independently — which they need, because their load profiles have nothing in common.

## What it is safe to rely on

**Safe.** Anything the indexer serves that is derived from committed blocks: fills, orders, positions, funding payments, transfers, market data. These are reshaped from chain output.

**Not the same thing.** Anything not yet committed. An order accepted into the [mempool](/protocol/architecture/mempool) has not been ordered, and the indexer has nothing to say about it. Absence from the indexer means not-yet-committed, not rejected.

**Verifiable.** If an answer matters enough — a settlement dispute, an audit, an accounting reconciliation — it can be checked against the chain directly rather than taken from the indexer. Running your own full node is the strongest form of this, and is what a participant who cannot afford to be wrong should do. See [Run a node](/developers/run-a-node).

<Note>
  Two consumers reading the same committed range should get the same answer. If they do not, the discrepancy is in the serving path and is a bug to report — not an inherent property of reading a chain through an indexer.
</Note>

## Where to go next

<CardGroup cols={2}>
  <Card title="State model" href="/protocol/architecture/state/model">
    What the indexer is reading, and which representation is authoritative.
  </Card>

  <Card title="Developers" href="/developers/overview">
    REST and WebSocket surfaces, SDKs, and tools.
  </Card>

  <Card title="Run a node" href="/developers/run-a-node">
    Verifying against the chain yourself, and why the set is closed.
  </Card>

  <Card title="Program services" href="/protocol/architecture/programs">
    What consumes this stream to compute derived account state.
  </Card>
</CardGroup>
