> For the complete documentation index, see [llms.txt](https://docs.okkult.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.okkult.io/proof/how-it-works.md).

# How it works

## How it works

Okkult Proof generates a zero-knowledge compliance proof from an approved address set.

The full proving step runs in the browser. The address stays local. Only the proof and its public signals reach the chain.

### 1. The Compliance Set

The compliance set is a Poseidon Merkle tree of clean addresses.

This set is updated every `6 hours` from `OFAC API + Chainalysis Oracle` data.

### Tree properties

| Property              | Value                |
| --------------------- | -------------------- |
| Hash function         | `Poseidon`           |
| Tree depth            | `20`                 |
| Capacity              | `2^20 addresses`     |
| Update interval       | `Every 6 hours`      |
| On-chain root storage | `ComplianceTree.sol` |

The current root is stored on-chain in `ComplianceTree.sol`.

That root is the public reference used during proof verification.

### 2. The ZK circuit

The circuit is written in `Circom`.

It uses the `Groth16` proof system.

The circuit proves that a wallet address belongs to the current compliance set without revealing the address itself.

### Circuit inputs

| Input type     | Values                             |
| -------------- | ---------------------------------- |
| Private inputs | `address`, `secret`, `Merkle path` |
| Public inputs  | `root`, `nullifier`                |

### What the circuit proves

The circuit proves all of the following at the same time:

1. The private address is included in the compliance Merkle tree.
2. The Merkle path matches the public root.
3. The nullifier was derived from the private address and secret.

The verifier learns that the proof is valid.

The verifier does not learn the address, the secret, or the Merkle path.

### 3. The nullifier

The nullifier links one proof instance to one private secret pair.

It prevents reuse without revealing the underlying address.

```
nullifier === Poseidon(address, secret)
```

### Nullifier properties

| Property          | Value                                        |
| ----------------- | -------------------------------------------- |
| Formula           | `Poseidon(address, secret)`                  |
| Registry contract | `NullifierRegistry.sol`                      |
| Purpose           | Prevent the same proof from being used twice |
| Privacy effect    | Does not reveal the address or secret        |

When a proof is accepted, the nullifier is recorded in `NullifierRegistry.sol`.

Any later proof with the same nullifier is rejected.

### 4. On-chain verification

`OkkultVerifier.sol` verifies the proof on-chain.

It checks three things:

1. The root is valid and not stale.
2. The nullifier has not been used before.
3. The ZK proof is mathematically valid.

### Verification flow

```
a. Root is valid and not stale
b. Nullifier not previously used
c. ZK proof is mathematically valid
```

If all checks pass, the protocol issues a certificate that remains valid for `30 days`.

### Next Steps

* [Generate proof](/proof/generate-proof.md)
* [Proof validity](/proof/proof-validity.md)
* [Shield overview](/shield/overview.md)


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.okkult.io/proof/how-it-works.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
