# zkProofer Node - NaaS Provider Integration Guide

**The node Plug-in is open source here**

{% embed url="<https://github.com/humanity-org/hp-verification-node-plugin>" %}

***

## Step 1 — Prerequisites

* The owner's wallet address (License NFT holder)
* Native $H tokens for gas funding

### Hardware requirements

#### Container-Level Resource Limits

| Resource | Single Node / Multi-Node (up to 20 licenses) |
| -------- | -------------------------------------------- |
| CPU      | 0.5 vCPU (or shared/burstable)               |
| RAM      | 256 MB                                       |
| Storage  | 500 MB                                       |
| Network  | 10 Mbps                                      |

These limits apply to each container running a zk proofer node.

***

#### Minimum Machine Specs (Self-Host)

| Resource | Single Node / Multi-Node (up to 20 licenses) |
| -------- | -------------------------------------------- |
| CPU      | 1 vCPU                                       |
| RAM      | 512 MB                                       |
| Storage  | 5 GB                                         |
| Network  | 10 Mbps                                      |
| OS       | Linux (Ubuntu 22.04+), macOS, Windows (WSL2) |
| Runtime  | Docker 24+                                   |
| Arch     | x86\_64 (amd64) or ARM64                     |

***

## Step 2 — Pull the Image

```
ghcr.io/humanity-org/hp-verification-node-plugin:latest
```

```bash
docker pull ghcr.io/humanity-org/hp-verification-node-plugin:latest
```

***

## Step 3 — Configure & Start the Node

### Environment variables

| Variable           | Required | Description                                                                                  |
| ------------------ | -------- | -------------------------------------------------------------------------------------------- |
| `OWNERS_ALLOWLIST` | Yes      | The owner's wallet address (License NFT holder)                                              |
| `ETH_PRIVATE_KEY`  | No       | Burner wallet private key (hex, no `0x` prefix). If omitted, auto-generated on first launch. |
| `LOG_LEVEL`        | No       | `info` (default) or `debug`                                                                  |

### Volume

| Container path | Purpose                                                                                                                       |
| -------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `/app/cache`   | Stores burner wallet data. Must be persisted — losing this volume means losing the burner wallet and requiring re-delegation. |

### Single node — docker run

```bash
docker run -d --pull always \
  -e OWNERS_ALLOWLIST="<owner_wallet_address>" \
  -e LOG_LEVEL="info" \
  -v /data/node-cache/<owner_id>:/app/cache \
  --name humanity-node-<owner_id> \
  ghcr.io/humanity-org/hp-verification-node-plugin:latest
```

### Multiple nodes — Docker Compose

```yaml
services:
  node-owner-1:
    image: ghcr.io/humanity-org/hp-verification-node-plugin:latest
    pull_policy: always
    environment:
      - OWNERS_ALLOWLIST=0xOwner1WalletAddress
      - LOG_LEVEL=info
    volumes:
      - ./data/owner-1:/app/cache
    restart: unless-stopped

  node-owner-2:
    image: ghcr.io/humanity-org/hp-verification-node-plugin:latest
    pull_policy: always
    environment:
      - OWNERS_ALLOWLIST=0xOwner2WalletAddress
      - LOG_LEVEL=info
    volumes:
      - ./data/owner-2:/app/cache
    restart: unless-stopped
```

***

## Step 4 — Retrieve the Burner Wallet Address

On first launch the container generates a burner wallet and writes it to `/app/cache/flohive-cache.json`:

```json
{
  "burnerWallet": {
    "privateKey": "<hex_no_0x_prefix>",
    "address": "<0x_address>"
  }
}
```

Read the address from the cache file:

```bash
cat <mounted-cache-dir>/flohive-cache.json | jq -r '.burnerWallet.address'
```

Or read it directly from the startup logs — the node prints it on every start:

```
2026-01-01T00:00:00.000Z  [INFO]   loaded config  {"network": "ethereum",
  "heartbeatPeriodSeconds": 3600, "ownersAllowList": ["0xYourOwnerAddress"],
  "eth.chainId": 629274, "eth.gasLimit": 6000000}
2026-01-01T00:00:00.001Z  [DEBUG]  initializing node ...
2026-01-01T00:00:00.002Z  [DEBUG]  generating burner wallet
2026-01-01T00:00:00.003Z  [DEBUG]  node cache does not exist, initializing empty node cache
2026-01-01T00:00:00.004Z  [DEBUG]  burner wallet cache written  {"file": "/app/cache/flohive-cache.json"}
2026-01-01T00:00:00.005Z  [DEBUG]  generated burner wallet  {"address": "0x44..."}
2026-01-01T00:00:00.006Z  [INFO]   Using burner wallet  {"address": "0x44..."}
2026-01-01T00:00:00.007Z  [DEBUG]  loaded plugin  {"path": "plugins/signature-scan-demo.so"}
2026-01-01T00:00:00.008Z  [DEBUG]  using delegation hub  {"address": "0x3F1B..."}
2026-01-01T00:00:00.009Z  [DEBUG]  starting delegations offer check
2026-01-01T00:00:00.010Z  [DEBUG]  received delegation offers  {"delegation offers": 0}
2026-01-01T00:00:00.011Z  [DEBUG]  starting heartbeats
2026-01-01T00:00:00.012Z  [WARN]   no delegations, skipping heartbeat
```

`[WARN] no delegations, skipping heartbeat` is expected at this stage — the node is running but nothing is delegated yet.

If `ETH_PRIVATE_KEY` is set, the container skips key generation and uses that key instead. Useful when redeploying a node that already has an active delegation.

***

## Step 5 — Fund the Burner Wallet

Send native $H tokens to the burner wallet address. The node uses this balance to pay gas for on-chain verification proofs.

* Minimum recommended balance: \~0.1 $H (roughly 1 week of operations)
* Estimated $H per node per year: \~5 $H/year

***

### Key Rules

* **One container = one License owner.** Each node instance maps to one owner wallet address. One owner can delegate up to 20 License NFTs to the same node.
* **Heartbeat requirement.** The node sends a heartbeat every hour (`heartbeatPeriodSeconds: 3600`). A minimum of 22 successful heartbeats per day — out of 24 possible — is required to qualify for daily rewards. Keep containers running continuously.
* **Gas.** A node costs roughly 5 $H per year to run. Keep the burner wallet above 0.1 $H — a node without gas can't submit proofs.

***

## Step 6 — Delegate the License NFT

The owner delegates their License NFT(s) to the burner wallet address. Rewards only start accruing once delegation is active.

Delegation can be done via the Node Dashboard or programmatically via on-chain contract calls.

### Contracts

* **DelegationHub** — manages delegation between owners and nodes

  * Testnet: `0x1aEbB36b9A6B33E377319a7E2d928BE54d0cB68e`

  **License NFT** — ERC-721 contract for node License NFTs

  * Testnet: `0xF04f1062D70432d167EB9f342b98063228c6b496`

#### Check pending delegation offers

```
Function: getIncomingDelegationOffers(address to, uint256 offset, uint256 limit)
Selector: 0x49ceece3
```

Parameters:

* `to` — the node's burner wallet address
* `offset` — pagination offset (start at 0)
* `limit` — max results (e.g. 100)

Returns `DelegationOffer[]`, each containing:

* `hash` (bytes32)
* `to` (address) — burner address
* `from` (address) — owner address
* `tokenId` (uint256) — License NFT ID
* `commissionPercentage` (uint32)
* `enabled` (bool)

A valid pending offer has `enabled == true` and `from` matching the expected owner address.

***

## Step 7 — Verify Delegation is Active

Call `getIncomingDelegations` on the DelegationHub to confirm a License is actively delegated to the burner address.

```
Function: getIncomingDelegations(address to, uint256 offset, uint256 limit)
Selector: 0xe2ae2879
```

Parameters:

* `to` — the node's burner wallet address
* `offset` — pagination offset (start at 0)
* `limit` — max results (e.g. 100)

Returns `Delegation[]`, each containing:

* `hash` (bytes32)
* `to` (address) — burner address
* `tokenId` (uint256) — License NFT ID
* `commissionPercentage` (uint32)
* `enabled` (bool)

For each entry where `enabled == true`, call `ownerOf(tokenId)` on the License NFT contract and confirm the returned address matches the expected owner.

#### Verify License NFT ownership

```
Function: ownerOf(uint256 tokenId)
Selector: 0x6352211e
Contract: License NFT
```

Returns the current owner address of the given License NFT.

Once delegation is confirmed, the logs should show:

```
[DEBUG]  received delegation offers  {"delegation offers": 1}
[DEBUG]  accepting delegation offer  {"hash": "cf9cede70c..."}
[DEBUG]  transaction submitted  {"hash": "0x7f396478..."}
[INFO]   delegation offer accepted  {"hash": "cf9cede70c..."}
```

***

## Step 8 — Monitor

Use whichever monitoring stack your platform already runs. Here's what to watch for.

### Container health

The node produces two layers of logs: base node logs (`[INFO]`, `[DEBUG]`, `[WARN]`) and verification plugin logs (`[VerificationPlugin]`). The plugin layer confirms that actual verification jobs are running — separate from the node's own startup and delegation activity.

| Signal                                                 | What it means                                                   |
| ------------------------------------------------------ | --------------------------------------------------------------- |
| Container exits or restarts unexpectedly               | Check logs for `[ERROR]` entries                                |
| `[INFO] Using burner wallet` on startup                | Node started correctly                                          |
| `[INFO] delegation offer accepted`                     | Delegation confirmed, node is active                            |
| `[WARN] no delegations, skipping heartbeat`            | No delegation yet — expected before the owner delegates         |
| `[VerificationPlugin] SUCCESS: Verification completed` | Node is actively completing verification jobs                   |
| `[VerificationPlugin] ERROR:`                          | Verification job failed — check for API errors or auth issues   |
| `SKIPPED: Node version too old`                        | Image is outdated — pull the latest version                     |
| Any `[ERROR]` line                                     | Investigate — API errors, auth issues, or connectivity problems |

### Gas balance

Monitor the burner wallet's native $H balance on-chain. When it drops below your threshold, auto-fund from your platform treasury wallet.

* Alert threshold: 0.1 $H (roughly 1 week of operations at \~5 $H/year).

### Heartbeat

A healthy node produces 22+ heartbeats per day. If logs show no heartbeat activity for an extended period, check container health and network connectivity.

***

### Support & Contact

For image access, integration issues, or production credentials, contact the Humanity Protocol team at <support@humanity.org>.


---

# Agent Instructions: 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.humanity.org/zkproofer-node/zkproofer-node-naas-provider-integration-guide.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.
