> ## 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.

# Program services

> Services that derive account state from committed trading history off-chain and commit the result back through protocol transactions.

Some things a venue needs to know about an account cannot be computed while a block executes. A volume tier depends on thirty days of trading. A reward depends on a window that has not closed. A referral attribution depends on a relationship established months ago.

Putting that work inside block execution would be wrong twice over: it would make every block pay for a computation almost no block needs, and it would require the kernel to carry history it has no other reason to hold.

Program services solve it by inverting the direction. The computation runs **outside** the block, over the committed record. Its *result* is then committed **back** to the chain as protocol state, where execution can read it in constant time like any other configuration.

<div className="dg" data-dg="program-writeback">
  <div className="dg-c" style={{aspectRatio:"720 / 348"}}>
    <svg className="dg-w" viewBox="0 0 720 348" aria-hidden="true">
      <path className="dg-wire" d="M 144.00 70.00 L 169.60 70.00" />

      <path className="dg-head" d="M 176.00 70.00 L 169.60 74.40 L 169.60 65.60 Z" />

      <path className="dg-wire dg--sky" d="M 344.00 70.00 L 369.60 70.00" />

      <path className="dg-head dg--sky" d="M 376.00 70.00 L 369.60 74.40 L 369.60 65.60 Z" />

      <path className="dg-wire" d="M 534.00 70.00 L 561.75 38.78" />

      <path className="dg-head" d="M 566.00 34.00 L 565.04 41.71 L 558.46 35.86 Z" />

      <path className="dg-wire dg--blue" d="M 534.00 70.00 L 561.33 95.62" />

      <path className="dg-head dg--blue" d="M 566.00 100.00 L 558.32 98.83 L 564.34 92.41 Z" />

      <path className="dg-wire dg--blue" d="M 645.00 128.00 L 645.00 136.00 L 465.00 136.00 L 465.00 142.00" />

      <path className="dg-head dg--blue" d="M 465.00 142.00 L 460.60 135.60 L 469.40 135.60 Z" />

      <path className="dg-wire dg--green" d="M 554.00 181.00 L 567.60 181.00" />

      <path className="dg-head dg--green" d="M 574.00 181.00 L 567.60 185.40 L 567.60 176.60 Z" />
    </svg>

    <div className="dg-b" style={{left:"0.0000%",top:"11.4943%",width:"19.4444%",height:"17.2414%"}}><span className="dg-t">Committed trading history</span></div>
    <div className="dg-b dg--sky" style={{left:"25.0000%",top:"5.7471%",width:"22.2222%",height:"28.7356%"}}><span className="dg-t">Program service</span><span className="dg-s">windowed computation, outside the block</span><span className="dg-n">reads the fee table live from the chain</span></div>
    <div className="dg-b dg--yellow dg-round" style={{left:"52.7778%",top:"10.3448%",width:"20.8333%",height:"19.5402%"}}><span className="dg-t">Changed since last applied?</span></div>
    <div className="dg-b" style={{left:"79.1667%",top:"2.8736%",width:"20.8333%",height:"13.7931%"}}><span className="dg-t">Nothing written</span></div>
    <div className="dg-b dg--blue" style={{left:"79.1667%",top:"21.8391%",width:"20.8333%",height:"13.7931%"}}><span className="dg-t">Protocol transaction</span></div>
    <div className="dg-b dg--green" style={{left:"52.7778%",top:"41.9540%",width:"23.6111%",height:"20.1149%"}}><span className="dg-t">Chain state</span></div>
    <div className="dg-b dg--green" style={{left:"80.2778%",top:"41.9540%",width:"19.7222%",height:"20.1149%"}}><span className="dg-t">Read during execution</span><span className="dg-s">in constant time</span></div>
    <div className="dg-b dg-dashed dg-left" style={{left:"0.0000%",top:"70.1149%",width:"48.8889%",height:"24.1379%"}}><span className="dg-t">Diffing is on rates, not tier positions</span><span className="dg-s">A threshold that moves, or a tier that is re-priced, changes what an account pays without changing its tier index.</span></div>
    <div className="dg-b dg-dashed dg-left" style={{left:"51.1111%",top:"70.1149%",width:"48.8889%",height:"24.1379%"}}><span className="dg-t">The chain resolves the final rate</span><span className="dg-s">A period is recorded as applied only after every batch commits; a crash replays the whole period, which is safe because the computation is idempotent.</span></div>
  </div>
</div>

The chain remains the authority. A service does not hold state the network depends on — it proposes a value, and only what the chain accepted is real.

## What runs today

**Volume-based fee tiers.** The service consumes trading history streamed from a node, accumulates per-account volume, and snapshots it on a schedule. When a period closes it computes each account's rolling-window volume, maps that through the fee configuration **read live from the chain**, and writes changed accounts back in batches.

Several details in that sentence are load-bearing:

* **The tier table is read from the chain, never hard-coded.** A service holding its own copy would keep applying yesterday's schedule after the network changed it.
* **Diffing is against rates, not tier positions.** Comparing tier *indices* misses two real cases: a threshold moving so that an unchanged account lands in a different tier, and a tier being re-priced while its index stays the same. Both change what an account pays; neither changes its index.
* **The chain resolves the final rate.** The transaction carries a tier index; execution resolves it against the live fee configuration. A tier index outside the valid range fails the whole batch rather than partially applying.
* **A period is recorded as applied only after every batch commits.** A crash mid-period replays the whole period, which is safe because the computation is idempotent — the same window produces the same result.

## The failure model

These services sit between two systems that will each be unavailable sometimes. The design assumes it rather than treating it as exceptional.

Dependency failures — the database, the node's stream, the node's API — are retried with backoff. They do not end the process, because a restart does not fix an unreachable dependency; it just adds a cold start to the outage. What stays fatal is what a restart *can* fix or an operator must see: invalid configuration at startup, an inability to bind the health endpoint, and panics.

During an outage the process stays running, reports itself not-ready, and counts errors. The operational signal is therefore **"has this been not-ready for N minutes"** rather than **"is the process alive"** — which is the useful question, since a live process that has been failing to ingest for an hour is the actual incident.

Shutdown is graceful on the signals an orchestrator sends: work stops, checkpoints flush, and the process exits cleanly. Without that, every routine deployment would cost an unflushed window and a replay.

<Note>
  A period that has been computed but not yet applied is not a lost period. Because the computation is idempotent and the applied period is recorded only after the write succeeds, an interrupted run resumes by redoing the window rather than skipping it.
</Note>

## Why the pattern generalizes

The write-back path is generic. Protocol transactions exist for setting account-level configuration and for setting global configuration, and a program service is any process that computes a value for one of them from committed history.

Fee tiers is the only one running. Incentive programs, referral attribution, and campaign eligibility would have the same shape: a windowed computation over trading history, a diff against what is currently applied, and a batched write-back. They would belong here rather than in the kernel for the same reason fee tiers does — the computation is periodic and historical, while execution needs the answer to be a constant-time lookup. None of them is built; the pattern is what generalizes, not a commitment that they will use it.

The commercial terms of these programs live in [Fees & Programs](/programs/fees). This page is about how the result gets on-chain.

## Where to go next

<CardGroup cols={2}>
  <Card title="Indexer" href="/protocol/architecture/indexer">
    The stream these services consume.
  </Card>

  <Card title="Fees" href="/programs/fees">
    The commercial side: what the tiers are and what they cost.
  </Card>

  <Card title="IntentionKernel" href="/protocol/architecture/kernel">
    How configuration written back is read during execution.
  </Card>

  <Card title="State model" href="/protocol/architecture/state/model">
    Why configuration keys are versioned, and why clients should resolve them live.
  </Card>
</CardGroup>
