Skip to main content

The five onboarding phases

Onboarding is an ordered pipeline — each phase depends on the previous.
  1. Pre-flight — clock sync, iroh key generation, Ethereum key funding, region selection
  2. On-chain setup — TOKEN approval, staking, atomic registerNode
  3. Node startup — rate-bounds fetch, blacklist sync, peer table bootstrap from registry
  4. Gossip subscription — join cdn/global/v1 and regional topic, publish first NodeAnnounce
  5. Accepting paid delivery — seven acceptance criteria below

Phase 1: Pre-flight

  • NTP sync. Gossip validation requires ±60 s agreement for NodeAnnounce freshness. A skewed clock will cause your announcements to be rejected silently.
  • iroh keypair. decdn node init-keys. Store securely — loss means recovery via reclaimNodeId (requires proving Ethereum address ownership).
  • Ethereum key funding. Enough TOKEN for minStake + buffer for challenge bonds. Enough native gas for ~20 transactions (registration, stake top-up, occasional updates).
  • Region. Two-letter country code. Pick where your node is physically located; misrepresenting region creates reputation + compliance exposure.

Phase 2: On-chain setup

1. TOKEN.approve(StakingRegistry, minStake)
2. StakingRegistry.registerNode(
       nodeId,               // ed25519 pub key
       multiaddrs,           // [QUIC multiaddr string, ...]
       ethOwnershipSig,      // EIP-712 signature binding NodeId to Ethereum address
       ed25519OwnershipSig,  // ed25519 signature over (Ethereum address, nonce)
       stakeAmount
   )
The registerNode call is atomic:
  • Pulls stakeAmount TOKEN from your address (must be ≥ minStake).
  • Verifies the EIP-712 signature (your Ethereum address committing to your NodeId).
  • Verifies the ed25519 ownership proof (your NodeId committing to your Ethereum address, preventing squatting).
  • Writes the NodeInfo record.
Fails if any step fails. You re-try from scratch.

Phase 3: Node startup

On decdn node run:
  1. Load keys and verify the registered NodeInfo matches your local identity.
  2. Fetch (MIN_RATE, MAX_RATE) for your payment token from the governance contract.
  3. Sync the ContentBlacklist — read from genesis to current block. Subsequent polls happen on the configured interval.
  4. Bootstrap the peer table by querying StakingRegistry.getNodes() for known nodes. Fallback to a bundled peers.json if the registry is unreachable.

Phase 4: Gossip subscription

  1. Join cdn/global/v1.
  2. Join cdn/region/{cc}/v1 for your region.
  3. Publish your first NodeAnnounce (rate, multiaddrs, region, popular_hashes = []).
  4. Join cdn/reputation/v1 to receive gossip-propagated reputation reports.

Phase 5: Accepting paid delivery — seven acceptance criteria

Your node is operationally ready only when all of these hold:
  1. Registered in StakingRegistry with stake ≥ minStake.
  2. Gossip subscribed — both global and regional topics.
  3. First NodeAnnounce published within the last 60 seconds.
  4. Blacklist sync lag < 5 minutes (decdn_blacklist_sync_lag_seconds).
  5. Rate within bounds — no decdn_rate_bounds_clamp_events_total increments.
  6. Watchtowers registered — 2–3 independent watchtowers (watchtower setup).
  7. Peer table populated — ≥ N peers (configurable threshold).
/health returns "ready" only when all seven hold.

NAT and multiaddr updates

iroh hole-punching handles most NAT scenarios automatically. When your public-facing address changes (cloud region, new home ISP lease), call StakingRegistry.updateMultiaddrs(newMultiaddrs) — gossip propagation carries the update to the rest of the mesh.

Re-onboarding after deregistration

If you deregister (voluntary) or are auto-ejected (prolonged offline + governance policy), re-registering requires:
  • Nonce increment. NodeInfo.nonce is incremented to invalidate old slash signatures.
  • firstRegisteredAt preserved. Your tenure anchor does not reset — reputation cold-start bonuses do not apply twice to the same operator address.

Key rotation

bindNodeId() lets you rotate keys post-registration. Workflow:
  1. Generate a new keypair.
  2. Sign (Ethereum address, new NodeId, nonce+1) with both the new NodeId and the Ethereum address.
  3. Submit to bindNodeId.
  4. Contract updates the NodeInfo.nodeId field atomically.
Existing channels tied to the old NodeId continue to operate — settlement addresses are the Ethereum address, not the NodeId.

Deregistration

StakingRegistry.deregisterNode(nodeId)
Begins an unbonding period after which stake is withdrawable via claimUnbondedStake. During unbonding:
  • Node cannot accept new deliveries (automatically excluded from selection).
  • Node is still slashable for evidence that predates deregistration.
  • MAX_EVIDENCE_AGE_US is strictly less than the unbonding period — evidence that could slash you must be submitted before you can withdraw.
The unbonding period is set by governance, bounded in the safety range.