# The nine security layers

> GHAN has nine independent layers between a member application and a harmful or fraudulent install. Any one of them failing does not open the network, because they defend different things. This page lists all nine, says which are live today, and states plainly what the SDK cannot do.

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

---

## The nine layers

They are listed in the order a bad actor would meet them. Each defends something the others do not, which is the point of having nine rather than one good one.

| # | Layer | What it stops | Status |
|---|---|---|---|
| 1 | **Gate** | An unsigned, malicious or disposable app entering the network at all | Specified; VirusTotal and RDAP wired |
| 2 | **Placement** | A partner reaching your users with anything but a templated card | Specified in the SDK contract |
| 3 | **Attribution** | Installs being credited that cannot prove where they came from | Signing and canonical JSON live and tested |
| 4 | **Device** | Install farms, virtual machines, datacenter claims | Offline ASN and datacenter detection live |
| 5 | **Economics** | Fraud that survives every technical check but is unprofitable anyway | Ledger and escrow schema live |
| 6 | **Behaviour** | Apps that look clean per-chain but wrong in aggregate | Specified |
| 7 | **Enforcement** | A detected problem continuing to cost partners money | Specified; kill switch in the SDK contract |
| 8 | **Data** | The record of what happened being edited after the fact | **Live and proven on a real database** |
| 9 | **Disclosure** | Any of the above quietly not being applied | Live — this page, and the reason codes |

"Specified" means designed and written down, not yet running against live traffic, because GHAN has not traded. The [status page](/status/) is the authoritative list of what exists today.

---

### Layer 1 — The gate

Five automated checks, run at registration and **re-run every week**, because an app that was clean when it joined is not clean forever.

1. A notarised macOS build, or a valid Windows Authenticode chain.
2. A clean [VirusTotal](https://www.virustotal.com/) result — no malicious or suspicious detections above threshold.
3. A domain at least six months old, verified over RDAP. It is the cheapest check a disposable operation cannot pass, because the one thing it is trying to save is time.
4. A contact email that answers a challenge.
5. A download URL that actually serves the bundle identifier the app declared.

Failures return machine-readable reason codes, not prose. Detail: [vetting and scoring](/vetting/).

### Layer 2 — The placement layer

What the SDK renders is fixed: partner icon, partner name, one generated line, an install button, a dismiss affordance. That is the complete set.

Five properties, stated as implementation facts so they can be checked rather than believed:

1. **Cannot render remote markup.** The placement payload has no field for HTML, JavaScript or a script URL.
2. **Cannot install anything.** The SDK opens a URL. The person completes the other app's installer, or does not.
3. **Cannot show a card you did not request.** Nothing renders unless your code calls `ghan.slot()`.
4. **Cannot bypass your consent state.** Telemetry is gated on a value your application passes in.
5. **Cannot appear inside an installer.** There is no installer-time slot, and adding one is ruled out by [Commandment III](/manifesto/).

Plus your own controls, enforced client-side in your build: category and named-app exclusions, and a frequency cap. GHAN cannot override them from the server because there is no server field that does.

### Layer 3 — Attribution

A single-use, 15-minute, ed25519-signed token, countersigned by the receiving application's own key. Single use is a unique database constraint rather than an `if` statement, because an `if` statement has a race condition.

Two independent signatures are required before a chain can advance past the claim. Neither party can produce the other's; neither can GHAN. No fingerprinting, no probabilistic matching, no fallback. Detail: [deterministic attribution](/attribution/).

### Layer 4 — Device signals

Attached to every install claim: virtual-machine and hypervisor indicators, operating-system install age, a **salted** hardware hash — never the raw identifier — and clock skew.

The claiming IP's country and autonomous system are resolved from an **offline** database, so no partner IP address is sent to a third party on the hot path. A desktop install claim arriving from a hosting provider's AS is an install farm, and that is a stronger signal than city-level geography.

These signals exist to kill fraudulent chains, not to identify people, and they are deliberately worse at identifying people than the fingerprinting GHAN refuses to perform.

### Layer 5 — Economics

The layer that does the most work, because rules alone lose to a motivated attacker who studies them.

Every monetised feature has to satisfy one inequality before it ships:

> The cost to fake an event must exceed the payout for it.

Faking one cleared install means forging an ed25519 signature belonging to an SDK inside another company's signed binary, on a machine that does not look virtualised, from an IP that is not a hosting provider, with a plausible click-to-install time, that is then used again two days later, inside a pair whose net flow is capped at EUR 500 a month. The payout is a share of EUR 12 to EUR 50.

That inequality has already removed installer-time placements, custom creatives, probabilistic attribution and direct app-to-app targeting from the product.

### Layer 6 — Behaviour

Per-chain checks can be passed. Aggregates are much harder to fake.

- **Cohort retention shape** compared against the category baseline. A suspiciously perfect curve holds clearing, as does a cliff.
- **Canary probes** — the server sends signed liveness requests and the SDK answers with its own signature, proving a slot is genuinely live in a shipped build rather than merely declared in a dashboard.
- **Complaint rate**, tracked per app, feeding the behaviour score.

### Layer 7 — Enforcement

| Step | Trigger | Effect |
|---|---|---|
| Flag | Any rule fires | Logged, visible in `get_stats` |
| Hold clearing | Retention shape, geo anomaly | Chains stop clearing, escrow stays held |
| Throttle | Repeated device rejects, low behaviour score | Placement volume cut |
| Suspend + freeze | Canary failure, token forgery, confirmed collusion | All placements off, escrow frozen |

Plus the **kill switch**: the server disables every placement for an app instantly, honoured on the next fetch, no release required. It exists for the day you need to answer a reviewer in minutes rather than in a release cycle.

Every action is reversible by an administrator and visible to the app it happened to. There is no silent throttle.

### Layer 8 — Data

The only layer that is fully live and proven today.

- `UPDATE`, `DELETE` and `TRUNCATE` on the `ledger` table are **blocked by database trigger**, with a test that proves all three fail against a real Postgres instance before merge. Corrections are new lines.
- Row-level security on every table; the public API roles revoked.
- TLS verified against a **pinned root certificate**, not with verification disabled.
- Private signing keys generated on end-user devices and never transmitted.
- Secrets in environment variables only; nothing in source control.
- Every fraud rule and every ledger mutation has a test that runs before merge.

### Layer 9 — Disclosure

A control nobody can see is a control nobody can rely on.

- All fourteen [rejection and reversal conditions](/research/desktop-install-network-safety-and-fraud/) published, each with a stable reason code.
- `get_stats` returns `fraud_rejected` broken down by reason code — transparency applies to the installs you were **not** paid for.
- A monthly public fraud report generated from the flag table and the ledger, not written by hand.
- `get_network_stats` public and unauthenticated, so the clearing rate and fraud percentage need no account to read.

---

## What the SDK collects

**Behavioural counters, only after consent:** session start and end, coarse feature counts defined by the host application, whether a card was dismissed.

**Fraud signals, at install claim:** VM and hypervisor indicators, OS install age, a salted hardware hash, clock skew, and the claiming IP's country and ASN resolved offline.

**Never, under any circumstances:** file names, file contents, paths, window titles, clipboard contents, keystrokes, screen contents, raw hardware identifiers, MAC addresses, serial numbers, email addresses, advertising identifiers, cross-application identity, browsing history, or anything at all outside the host application.

GHAN's attribution is deterministic precisely so that it does not need identity. A network that refuses fingerprinting does not need a fingerprint.

## Consent and GDPR

The SDK's consent-safe mode emits nothing before the host app reports consent as true, and telemetry buffered before consent is **discarded rather than flushed**. A templated card can still render without consent, because rendering a card is not processing personal data — but you can gate that too. You are the controller; GHAN is the processor. See [privacy](/privacy/) and the [DPA](/dpa/).

## The answer for a reviewer

If somebody asks whether you shipped adware, this is the checkable version:

> The app includes the GHAN SDK, which shows an occasional card recommending another notarised, malware-scanned desktop application, at moments the app itself chooses, after installation and never inside an installer. Nothing is pre-checked and nothing installs without the person completing the other app's own installer. The SDK cannot render remote code, collects behavioural counters only, emits nothing before consent, and can be turned off entirely from the server.

Every sentence in that paragraph corresponds to something on this page that a reviewer can verify from the SDK source or from a packet capture. That is the only kind of security claim worth making.

## Questions people ask about this

### Is GHAN adware?

No. Adware installs or displays software the person did not ask for, usually by bundling it into an installer with a pre-checked box. GHAN never touches an installer, never pre-selects anything, and nothing is installed unless the person clicks a card and then completes the other application's own installer themselves. Every app that can appear in a card has passed a notarisation or Authenticode check and a malware scan first, re-run weekly.

### Can GHAN push code into my application?

No. The SDK arrives as a dependency in a build you compile and sign yourself. At runtime it fetches a signed placement payload consisting of an app id, an icon URL, a name and one line of text - there is no field in that payload that carries markup or code, and no code path in the SDK that would execute one if there were. The only thing the server can push is the kill switch, which turns placements off.

### What are the security layers?

Nine. The gate (code signature, malware scan, domain age, contact challenge, binary identity). The placement layer (templated only, no installer, host-controlled). Attribution (single-use signed tokens, double signing). Device signals (VM, OS age, salted hardware hash, clock skew, datacenter ASN). Economics (48-hour clearing, escrow, pair caps, cost-to-fake above payout). Behaviour (cohort retention shape, canary probes, complaint rate). Enforcement (flag, hold, throttle, suspend, kill switch, clawback). Data (append-only ledger, row-level security, pinned TLS, no PII). Disclosure (published rules, reason codes, monthly fraud report).

### Where is the signing key stored?

In your application's own data directory on the end user's machine, generated on first run. The private key never leaves that machine and GHAN never receives it. Only the public key is registered, during the activation handshake.

### Is GHAN GDPR compliant?

The SDK ships a consent-safe mode in which the host application passes its own consent state and no telemetry is emitted until it is true. The data collected is behavioural counters and fraud signals rather than personal data, GHAN publishes what those fields are, and a data processing agreement is at /dpa/. As always, compliance depends on your own implementation and disclosure as the controller.

### What does GHAN do if a member app turns malicious?

It loses its gate pass at the next weekly re-run and stops trading. The kill switch disables its placements network-wide within one fetch cycle, escrow is frozen, and the event appears in the monthly public fraud report. The weekly re-run of the malware and signature checks exists precisely because an app that was clean at registration is not clean forever.

### What data does the SDK collect?

Behavioural counters after consent - session starts and ends, coarse feature counts, card dismissals - and fraud signals attached to an install claim, which are VM indicators, OS install age, a salted hardware hash and clock skew. Never file contents, window titles, clipboard, keystrokes, raw hardware identifiers, or anything personally identifying.

