Skip to content

The project manifest

A project is a worker module plus a signed project manifest. The manifest has three tiers:

Tier Paths Effect of a change
Consensus consensus.* Needs an on-chain version and takes effect at the first epoch starting at least activation_delay_secs (1 hour) after publication
Scheduling scheduling.* Signed; effective immediately; no chain transaction
Listing listing.* Signed; effective immediately; store only

This is the manifest of the live reference project Hash Chain Lab (vault and icon shown as published):

{
"v": 1,
"network": "necter-testnet",
"chain_id": 11155111,
"developer": "0x78248efc7f73d98b5eba157d2c719a5aa9e9c1ff",
"slug": "hashchain-lab",
"version": 1,
"previous": null,
"consensus": {
"modules": { "worker": "0xa99c85c5c7c08f302256cad48b51d7c363a6fcea09d5d48e570c711eed8e6d90", "verifier": null },
"work": {
"class": "deterministic",
"verification": "redundant-execution",
"execution": "committee",
"functions": ["work"],
"task_source": { "kind": "schedule", "function": "work", "interval_secs": 60, "gas_limit": 2000000 },
"max_gas_limit": 10000000,
"committee": { "size": 3, "backups": 0, "min_size": 3,
"selection": "collateral-reputation-sortition-v1", "weight_cap_multiple": 10 },
"round_secs": 60,
"lease_secs": 30,
"epoch_secs": 3600,
"dispute_window_secs": 3600
},
"economics": {
"reward_token": "0x52efcdf0bd3ca7554c0dfb3141125b1a4d54eb7e",
"vault": "0x6ef3ba1ac3e43f03d26a7a70199418ef9a54de36",
"reward_model": "per-unit",
"reward_per_unit": "1000000000000000000",
"daily_emission": "24000000000000000000000",
"fee_split_bp": { "miner": 8500, "developer": 1000, "treasury": 500 },
"max_units_per_epoch": 1000000,
"max_recipients_per_epoch": 2000,
"min_collateral": "10000000000000000000",
"slashing_bp": { "invalid_result": 1000, "missed_sla": 0 }
}
},
"scheduling": {
"requirements": { "device_classes": ["desktop", "laptop", "phone", "server", "tablet"],
"cpu_cores": 1, "ram_mb": 512, "storage_mb": 64, "gpu": null,
"min_benchmark": 0, "engines": ["native", "pulley32", "pulley64"], "attestation": [] },
"sla": { "max_latency_ms": 5000, "min_uptime_bp": 0 },
"public_tasks": false
},
"listing": {
"name": "Hash Chain Lab",
"tagline": "A reference mining project: verifiable keccak hash chains",
"description": "The testnet's reference project. …",
"category": "compute",
"tags": ["example", "hashing", "reference"],
"icon": "https://testnet-rpc.necter.network/v1/assets/da678942d4656a903f61000551cc51c09464d7a223faaae304e8210e1ca19257",
"banner": null, "screenshots": [], "video": null,
"website": "https://testnet.necter.network", "docs": null, "support": null,
"features": ["Scheduled jobs every minute", "Committees of 3 miners", "Hourly epochs"],
"accent_color": "#FFC933"
}
}

Amounts are decimal strings in token base units (18 decimals for NECTA, so "1000000000000000000" = 1 NECTA). Unknown keys anywhere are rejected; null is allowed only where shown.

Field Rule
v 1
network "necter-testnet"
chain_id 11155111
developer your address, lowercase; it is part of the project id and cannot change
slug 3–48 chars, ^[a-z0-9](?:[a-z0-9-]{1,46}[a-z0-9])$; fixed forever
version 1 for a new project, then +1 per version
previous null for version 1, else the manifest_hash of the previous version
Field Rule
modules.worker Address of a deployed stateless module
modules.verifier null (resource-proof verifiers are coming soon)
work.class / verification / execution "deterministic" / "redundant-execution" / "committee" (other classes coming soon)
work.functions 1–64 functions of the worker that tasks may call, sorted, unique
work.task_source {"kind": "api"} (you submit tasks) or {"kind": "schedule", "function", "interval_secs": 10..3600, "gas_limit"} (the Hub creates one every interval)
work.max_gas_limit 1 .. 10^10
work.committee.size 3..31
work.committee.backups 0..16
work.committee.min_size 3..size; fewer eligible miners → validator fallback
work.committee.selection "collateral-reputation-sortition-v1"
work.committee.weight_cap_multiple 1..100
work.round_secs 10..600; lease_secs 5..round_secs
work.epoch_secs 3600 × m, m ∈ 24; can never change for a project
work.dispute_window_secs 3600..604800
economics.reward_token Any ERC-20 on Sepolia (test NECTA is a simple choice)
economics.vault Must equal the vault address predicted for your project id (prepare returns it)
economics.reward_model "per-unit" or "epoch-pool"
economics.reward_per_unit Amount per compute unit; "0" exactly when epoch-pool
economics.daily_emission Amount > 0: the most the project pays per day
economics.fee_split_bp miner + developer + treasury = 10,000; miner ≥ 5,000
economics.max_units_per_epoch 1..10^9; max_recipients_per_epoch 1..10,000
economics.min_collateral NECTA amount, ≥ 1 NECTA and ≤ 1,000,000 NECTA
economics.slashing_bp invalid_result 0..5000; missed_sla 0..500

How these numbers turn into payouts: Reward epochs and fees.

Field Rule
requirements.device_classes non-empty sorted subset of desktop, laptop, phone, server, tablet
requirements.cpu_cores, ram_mb, storage_mb, min_benchmark integers ≥ 0, checked against each device’s profile and benchmark score
requirements.gpu null (GPU requirements coming soon)
requirements.engines non-empty subset of native, pulley64, pulley32; all produce identical receipts, native is the native engine, pulley64/pulley32 the portable engine; exclude the portable engine only for speed
requirements.attestation [] (device attestation coming soon)
sla.max_latency_ms / sla.min_uptime_bp 100..600000 / 0..10000 (shown in the store)
public_tasks true lets anyone submit tasks (rate-limited per IP); false = only your API keys
Field Rule
name 1–64 bytes; tagline ≤ 120; description ≤ 8000
category one of blockchain, depin, machine-learning, storage, compute, bandwidth, data-sovereignty, iot, hardware-staking, content-delivery
tags ≤ 8, each ^[a-z0-9-]{1,24}$
icon required https URL ≤ 512 bytes
banner, video, website, docs, support https URL or null
screenshots ≤ 8 https URLs; features ≤ 12 strings of ≤ 120 bytes
accent_color #RRGGBB (upper-case hex) or null

Upload images to the Hub’s asset store (PNG, JPEG or WebP, ≤ 2 MiB, ≤ 4096×4096) and use the returned URL:

import base64
from necter_dev import api, login
tok = login()
a = api("POST", "/v1/developers/assets", tok,
json={"content_type": "image/png", "data_b64": base64.b64encode(open("icon.png", "rb").read()).decode()})
print(a["url"]) # https://testnet-rpc.necter.network/v1/assets/<sha256>
project_id = keccak256("necter-project-v1:" ‖ developer (20 raw bytes) ‖ slug)
manifest_hash = keccak256(canonical_json(manifest))
consensus_hash = keccak256(canonical_json(manifest.consensus))
signature = personal_sign (EIP-191) by `developer` over canonical_json(manifest)
vault = CREATE2 address from the vault factory with salt = project_id