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 |
A complete example
Section titled “A complete example”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.
Top-level fields
Section titled “Top-level fields”| 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 |
Consensus
Section titled “Consensus”| 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.
Scheduling
Section titled “Scheduling”| 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 |
Listing
Section titled “Listing”| 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 base64from necter_dev import api, logintok = 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>Derived values
Section titled “Derived values”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