# How GHAN works

> An install moves through GHAN as an event chain with six states. Each state transition requires cryptographic proof, money is held in escrow between claim and clearing, and any chain that cannot prove itself is refunded automatically rather than argued about.

Source: https://ghan.io/how-it-works/  
Published: 2026-09-22 · Updated: 2026-09-22  
Publisher: GHAN — the audited cross-promotion network for desktop apps (https://ghan.io)

---

## The six states of an event chain

Every GHAN transaction is one row in `event_chains` that moves through a state machine. It can only move forward, and each move has a precondition that must be proven, not asserted.

| State | What has to be true to enter it |
|---|---|
| `impression` | The serving SDK rendered a templated card and signed the render event |
| `click` | The person clicked; a single-use token was issued with a 15-minute expiry |
| `install_claim` | The receiving SDK found the token on first run and signed a claim with its own key |
| `activated_48h` | 48 hours later the receiving app is still installed and has been used |
| `cleared` | Escrow released, the serving app earned, the ledger line is final |
| `rejected` | A fraud rule or a missing signature killed the chain; the hold is refunded |

A chain cannot advance past `install_claim` with a single signature. The serving app's signature proves the placement happened; the receiving app's signature proves the install happened. Neither party can produce the other's.

## Step by step

### 1. The host app decides a moment has arrived

GHAN never decides when to interrupt someone. The host application calls `ghan.slot('first_success')` — or any of the other four [lifecycle slots](/lifecycle-slots/) — at a moment it considers appropriate. If the host never calls, GHAN never renders.

### 2. The SDK fetches a signed placement

The server returns a signed placement payload: a partner app id, an icon URL, a name, and one generated line of copy. It never returns HTML, JavaScript or a custom image. The SDK renders the card itself in the host application's own UI layer, which is why a GHAN card cannot carry a payload.

### 3. The impression is signed and recorded

The SDK signs the impression with the ed25519 private key it generated on first run and stored in the app data directory. That key never leaves the machine; only the public key was ever registered.

### 4. A click issues a single-use token

If the person clicks, the server issues a token with a nonce, a 15-minute expiry and a unique constraint on use. The partner's download URL opens with the token in the URL fragment, and the token is also written to a local handoff file that the partner's SDK reads on first run — because a browser download followed by a manual install loses the fragment.

### 5. The receiving app claims

On first run the partner's SDK looks for the token, via the `ghan://` protocol handler and the handoff file. It signs an `install_claim` with its own key and sends device signals alongside it: virtual-machine indicators, operating-system install age, a salted hardware hash that is never the raw value, and clock skew.

At this instant the advertiser's credits are charged and moved into escrow. No money has moved to anyone yet.

### 6. Forty-eight hours later, behaviour decides

The receiving SDK reports coarse usage counters — session starts and ends, feature counts, never content and never personal data. If the application is still present and has been used, the chain enters `activated_48h`, escrow releases, and the serving app earns.

If it has not, the hold refunds and the chain ends in `rejected` with a reason code the serving app can read through `get_stats`. Transparency applies to rejections too; a partner always learns why they were not paid.

## Where the money sits at each moment

1. **Before the claim** — the advertiser's credits are in their wallet. Nothing is reserved.
2. **At `install_claim`** — `escrow_hold` moves the CPI out of the wallet into escrow. The ledger records it.
3. **At `activated_48h`** — `escrow_release` plus `earn` credit the serving app with 70 percent of the CPI, or 84 percent of the value in credits. GHAN keeps 30 percent.
4. **At `rejected`** — the hold reverses. The ledger records the reversal as a new line; nothing is ever edited.

The `ledger` table blocks `UPDATE`, `DELETE` and `TRUNCATE` with a database trigger. A correction in GHAN is always a new line, never a rewritten one. That is what makes `get_ledger` worth reading.

## The three scoring layers

Before an app can trade at all it has to pass a [vetting gate](/vetting/), and after it starts trading its behaviour replaces its reputation:

- **Gate** — pass or fail, automated, run on registration and again weekly. Notarisation or Authenticode, a clean malware scan, a domain at least six months old, a verified contact email, and a download URL that serves the bundle id it declared.
- **Reputation** — a 0-100 score built from AI visibility and sentiment, release cadence, reviews, and domain and social coherence. It is what GHAN knows about an app before it has traded anything.
- **Behaviour** — after 500 installs traded, this replaces reputation entirely. Retention of referred installs, uninstall rate, canary compliance, complaint rate.

## What a partner's agent can verify without asking

- `get_ledger` returns raw signed lines with event-chain references.
- `get_stats` returns impressions, clicks, installs, cleared installs, retention and fraud rejections with reason codes.
- `get_network_stats` returns the published network-wide numbers — average CPI by category, clearing rate, fraud percentage.
- `get_score` returns the score breakdown with machine-readable reasons for every gate failure.

Every milestone GHAN ships has the same acceptance rule: a partner's agent must be able to verify the claim from those outputs alone.

## Questions people ask about this

### How long does it take for an install to clear?

A standard install clears 48 hours after the claim, once the receiving SDK has confirmed the application is still present and has been used. Pairs of apps trading more than EUR 500 per day with each other clear on day-7 engagement instead, which is a slower and stricter bar applied exactly where the incentive to cheat is largest.

### What happens if the referred user uninstalls within 48 hours?

Nothing clears and nobody is billed. The escrow hold placed at install_claim is released back to the advertiser automatically. The serving app earns nothing for that chain. This is the single most important design decision in GHAN - payout follows behaviour, never the install event.

### Can a serving app fake installs to earn credits?

It would have to forge two independent ed25519 signatures, one of which belongs to an SDK running inside another company's signed binary, then survive a click-to-install-time histogram, a device check that reads the ASN of the claiming IP, and a cohort retention curve compared against the category baseline. The cost of faking a cleared install is designed to exceed the payout for it, which is the test every monetised feature in GHAN has to pass before it ships.

### Does GHAN work if my app is offline most of the time?

Yes. Events are signed locally and queued, then delivered when connectivity returns. Token expiry is 15 minutes from issue, so a click made offline will not clear, but impressions, usage heartbeats and install claims all tolerate delayed delivery.

