From 74c60ceb4f30794f68becd6c6537e000e7e91923 Mon Sep 17 00:00:00 2001 From: Alper Gundogdu Date: Fri, 9 Oct 2026 11:09:00 +0100 Subject: [PATCH 1/3] docs: add run-a-node operator guide Nine pages for prospective node operators: overview, requirements, testnet funds, quickstart, bond and earnings, traffic, operating, compliance, and an FAQ. Redirect /node-setup to the quickstart. Show launch and post-activation FeeRouter shares in the governance bounds table. Co-Authored-By: Claude Opus 5.5 --- docs/README.md | 1 + docs/docs.json | 30 ++++++ docs/protocol/governance.mdx | 20 ++-- docs/run-a-node/bond-and-earnings.mdx | 55 ++++++++++ docs/run-a-node/compliance.mdx | 55 ++++++++++ docs/run-a-node/faq.mdx | 139 ++++++++++++++++++++++++++ docs/run-a-node/operating.mdx | 73 ++++++++++++++ docs/run-a-node/overview.mdx | 41 ++++++++ docs/run-a-node/quickstart.mdx | 128 ++++++++++++++++++++++++ docs/run-a-node/requirements.mdx | 48 +++++++++ docs/run-a-node/testnet-funds.mdx | 43 ++++++++ docs/run-a-node/traffic.mdx | 29 ++++++ 12 files changed, 652 insertions(+), 10 deletions(-) create mode 100644 docs/run-a-node/bond-and-earnings.mdx create mode 100644 docs/run-a-node/compliance.mdx create mode 100644 docs/run-a-node/faq.mdx create mode 100644 docs/run-a-node/operating.mdx create mode 100644 docs/run-a-node/overview.mdx create mode 100644 docs/run-a-node/quickstart.mdx create mode 100644 docs/run-a-node/requirements.mdx create mode 100644 docs/run-a-node/testnet-funds.mdx create mode 100644 docs/run-a-node/traffic.mdx diff --git a/docs/README.md b/docs/README.md index 9fb0c7a..c660aba 100644 --- a/docs/README.md +++ b/docs/README.md @@ -15,5 +15,6 @@ Then open the URL printed by the dev server. - `overview/` — what deCDN is, who participates, how it works - `protocol/` — technical reference +- `run-a-node/` — node operator guide: requirements, setup, earnings, operation, compliance, FAQ Source of truth for all technical claims is `decdn/adr/` in the sibling repo. diff --git a/docs/docs.json b/docs/docs.json index 8cd5c23..900a6c8 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -89,9 +89,39 @@ ] } ] + }, + { + "tab": "Run a node", + "groups": [ + { + "group": "Get started", + "pages": [ + "run-a-node/overview", + "run-a-node/requirements", + "run-a-node/testnet-funds", + "run-a-node/quickstart" + ] + }, + { + "group": "Run it", + "pages": [ + "run-a-node/bond-and-earnings", + "run-a-node/traffic", + "run-a-node/operating", + "run-a-node/compliance" + ] + }, + { + "group": "Help", + "pages": ["run-a-node/faq"] + } + ] } ] }, + "redirects": [ + { "source": "/node-setup", "destination": "/run-a-node/quickstart" } + ], "footer": { "socials": { "website": "https://decdn.org", diff --git a/docs/protocol/governance.mdx b/docs/protocol/governance.mdx index 42d2950..45eb88e 100644 --- a/docs/protocol/governance.mdx +++ b/docs/protocol/governance.mdx @@ -28,16 +28,16 @@ The phase ends when the multisig judges the operator set broad and diverse enoug Even governance cannot set parameters outside these bounds: -| Parameter | Default | Bounds | -| ----------------------------------------- | ---------------- | -------------------- | -| Operator revenue share (`FeeRouter` base) | 60% | 40% – 90% | -| Burn share (`FeeRouter`) | 30% (0% dormant) | 5% – 50% when active | -| Treasury share (`FeeRouter`) | 10% | 0% – 30% | -| Redemption grace window | 48 h | 48 h – 72 h | -| Per-operator voting cap | 5% | 1% – 25% | -| Trailing vote window | 13 weeks | 4 – 26 weeks | -| Capacity-curve exponent (α) | 1.2 | 1.0 – 1.8 | -| Unbonding window | 14 days | 7 – 60 days | +| Parameter | Default | Bounds | +| ----------------------------------------- | -------------------------------------------- | --------------------------- | +| Operator revenue share (`FeeRouter` base) | 90% at launch, 60% after activation | 40% – 90% | +| Burn share (`FeeRouter`) | 0% at launch (dormant), 30% after activation | 0%, or 5% – 50% when active | +| Treasury share (`FeeRouter`) | 10% | 0% – 30% | +| Redemption grace window | 48 h | 48 h – 72 h | +| Per-operator voting cap | 5% | 1% – 25% | +| Trailing vote window | 13 weeks | 4 – 26 weeks | +| Capacity-curve exponent (α) | 1.2 | 1.0 – 1.8 | +| Unbonding window | 14 days | 7 – 60 days | The three `FeeRouter` shares must sum to exactly 100% on every update. The burn share is the one that also accepts **zero**, so the buyback sink can be switched off entirely; what it cannot be is nominally on at a negligible share — either off, or at 5% or more. diff --git a/docs/run-a-node/bond-and-earnings.mdx b/docs/run-a-node/bond-and-earnings.mdx new file mode 100644 index 0000000..4418059 --- /dev/null +++ b/docs/run-a-node/bond-and-earnings.mdx @@ -0,0 +1,55 @@ +--- +title: Bond and earnings +description: How the TOKEN capacity bond is sized, what can be slashed, how unbonding works, and how a node earns USDC per byte delivered. +--- + +## The capacity bond + +A node bonds TOKEN in proportion to the bandwidth it declares. The curve is super-linear, so declaring more capacity than you can serve costs more than it is worth: + +```text +bond = max(minBond, 12.6 × Mbps^1.2) TOKEN +``` + +| Declared capacity | Curve value | +| ----------------- | --------------- | +| 10 Mbps | ≈ 200 TOKEN | +| 100 Mbps | ≈ 3,165 TOKEN | +| 250 Mbps | ≈ 9,500 TOKEN | +| 1 Gbps | ≈ 50,000 TOKEN | +| 10 Gbps | ≈ 795,000 TOKEN | + +`minBond` is a network-wide floor. **On the testnet it is 50,000 TOKEN**, so every declaration up to about 1 Gbps bonds 50,000 TOKEN. `decdn setup --dry-run` and `decdn node bond --mbps --dry-run` print the exact live figure. The team sends testnet TOKEN on request; see [Get testnet funds](/run-a-node/testnet-funds). + +To raise your declared capacity later, run `decdn node bond --mbps `. It tops the bond up to the new requirement. The full curve and its rationale are in [ADR 026 § Capacity-bond curve](https://github.com/decdn/decdn/blob/main/adr/026-tokenomics.md#capacity-bond-curve). + +## What can be slashed + +Only two offenses are slashable, and each needs signed, on-chain evidence: + +- **Rate manipulation.** Charging more than the rate your node signed in its probe response. +- **Blacklist violation.** Serving a blacklisted hash after its compliance window has passed. + +Corrupt or failed delivery is **not** slashable. The client verifies every chunk against its BLAKE3 hash, pays nothing for a bad range and fetches it from another node, and your node loses local reputation with that peer. See [Slashing](/protocol/slashing) and [Compliance and risk](/run-a-node/compliance). + +## Unbonding + +Unbonding takes **14 days**. The node goes inactive when the unbond starts, and the bond stays slashable for the whole window. To lower your capacity, run `decdn node unbond --to-mbps `. To exit fully, run `decdn node deregister`, then `decdn node unbond --all`, then run the unbond command again after the window to withdraw. + +## How you earn + +- **Per byte delivered.** Clients pay per megabyte in USDC through off-chain vouchers. A voucher pays only for bytes the client has verified. +- **Your rate.** You set your own per-MB rate with `payment.rate_per_mb` in USDC base units. The default, `10`, is $0.00001/MB, which is about **$0.01/GB**, the network's target client price. Clients choose the fastest node they can reach, so price within reason and compete on speed. A rate change takes effect after a restart. +- **Settlement.** The node redeems its vouchers on-chain automatically, by default every 5 minutes once a payment lane holds at least 1 USDC. `decdn node lanes` shows what has accrued. There is no claim step: the operator share goes to your address in the same transaction. + +## Operator share + +Each settlement passes through the on-chain `FeeRouter`, which splits it three ways: operator, buyback-and-burn, and treasury. + +| Network | Operator | Buyback-and-burn | Treasury | +| ------------------------ | -------- | ---------------- | -------- | +| Testnet (today) | 60% | 30% | 10% | +| Mainnet at launch | 90% | 0% | 10% | +| Mainnet after activation | 60% | 30% | 10% | + +Mainnet launches with the buyback bucket dormant, so operators keep 90%. Governance moves it to 60/30/10 only after the network meets set [activation criteria](https://github.com/decdn/decdn/blob/main/adr/018-liquidity-strategy.md). The testnet already runs the target split. See [Governance](/protocol/governance) for the bounds on each share. diff --git a/docs/run-a-node/compliance.mdx b/docs/run-a-node/compliance.mdx new file mode 100644 index 0000000..0b0a7c2 --- /dev/null +++ b/docs/run-a-node/compliance.mdx @@ -0,0 +1,55 @@ +--- +title: Compliance and risk +description: The operator terms, how the node follows the on-chain blacklist, regional obligations, your own refusal controls, and what can cost you bond. +--- + +Your node serves content from your own IP address. The node software is built to keep you compliant by default: it follows the on-chain blacklist automatically, scopes regional rules to the region you declare, and gives you local controls to refuse anything you choose. Your own obligations come from the law where you operate, and they apply on top of everything the software does. + +## The operator terms + +You accept the [operator terms](https://github.com/decdn/decdn/blob/main/crates/cli/TERMS.md) at registration. `decdn setup` shows them, and your acceptance is recorded on-chain as a hash of the exact text. In short, you agree to: + +- **follow the law that applies to you**, including any duty to report illegal content such as CSAM in your jurisdiction; +- **declare your region truthfully** and accept that region's takedown scope; +- **act on actual knowledge**: if you learn that specific content your node serves is illegal, stop serving it promptly and report it where your law requires. + +The terms do not, by themselves, require any particular automated scanning. What screening you must do, if any, is set by your law and your region. Read the full text before you register; it is short. + +## The blacklist + +The `ContentBlacklist` contract lists hashes no node may serve, and operators that sourced blacklisted content. Your node follows it on its own: it tails new entries live and re-reads the full list every 10 minutes, and it stops serving when its view of the chain is more than 30 minutes stale. + +| Entry type | Who adds it | Applies to | Compliance window | +| ---------- | ----------------------------- | ------------------------------ | ----------------- | +| Global | Governance | Every node | 24 h | +| Regional | That region's governance body | Nodes that declared the region | 24 h | +| Emergency | 3-of-5 emergency multisig | Every node | 2 h | + +The window is the time between an entry taking effect and it becoming slashable, so normal polling lag never costs you bond. A node running current software stays compliant without any action from you. See [Content takedown](/protocol/takedown). + +If you find a blacklisted hash in your cache, remove it at once with `decdn node evict `. + +## Your region + +The region you declare at registration decides which regional entries and which regional body apply to your node. Regional rules follow the region you declare, never the region of the client. Keep it truthful: a claim that measured latency contradicts costs reputation, and a region change only takes effect after a 7-day stability window, during which the old region's entries still bind you. + +## Refusing content yourself + +You can refuse any content locally, beyond what the blacklist requires. List hashes or origin operators in the `[content]` section of `node.toml`: + +```toml ~/.decdn/node.toml +[content] +denied_hashes = [""] +denied_origins = ["0x"] +``` + +`decdn node reload` applies the change without a restart. To stop serving entirely, stop the daemon: `decdn node drain --wait`, or `systemctl stop decdn-node`. + +## What can cost you bond + +Only two offenses are slashable: + +- **Rate manipulation.** Charging more than the rate your node signed in a probe response. +- **Blacklist violation.** Serving a blacklisted hash after its compliance window. + +The penalty is 5% of bond for the first offense, 15% for the second and 50% for the third and later, counted over the operator's lifetime. Unmodified node software commits neither offense. A slashed operator can appeal within 30 days with `decdn appeal slash`. Corrupt delivery, refusing a request, downtime and slow service are not slashable: they cost you payment and reputation, not bond. See [Slashing](/protocol/slashing). diff --git a/docs/run-a-node/faq.mdx b/docs/run-a-node/faq.mdx new file mode 100644 index 0000000..3d959b6 --- /dev/null +++ b/docs/run-a-node/faq.mdx @@ -0,0 +1,139 @@ +--- +title: Operator FAQ +description: Short answers to the questions prospective node operators ask most, each linking to the page with the details. +--- + +## Getting started + + + + Yes. The public testnet on Arbitrum Sepolia is permissionless: anyone can + bond, register and serve. See [Run a node](/run-a-node/overview). + + + Install `decdn` and `decdn-node`, run `decdn config init`, then `decdn setup + --dry-run` to preview and `decdn setup` to bond and register, then start + `decdn-node run`. See [Quickstart](/run-a-node/quickstart). + + + Linux under systemd is what we run ourselves. A Docker image is published at + `ghcr.io/decdn/decdn-node`. macOS and Windows builds work for testing. See + [Requirements](/run-a-node/requirements#operating-system). + + + We send testnet TOKEN directly. Send your EVM operator address through any + channel on our [contact section](https://decdn.org/#contact). Gas is + Arbitrum Sepolia ETH from any faucet; USDC comes from Circle's faucet. See + [Get testnet funds](/run-a-node/testnet-funds). + + + The [Discord](https://discord.gg/vVbNswKVGX), + [info@decdn.org](mailto:info@decdn.org), or [GitHub + issues](https://github.com/decdn/decdn/issues). + + + +## Hardware and network + + + + UDP 4433, for QUIC. Keep metrics (9090) and admin (9191) on localhost. See + [Requirements](/run-a-node/requirements#network). + + + No. The transport hole-punches through NAT and falls back to a relay. A + stable public address with UDP 4433 forwarded gives the fastest direct path. + When your address changes, the node updates its registration. + + + There is no protocol minimum. The recommended start is 4 vCPU, 8 GB RAM, an + SSD and 5 TB/month of egress. See + [Requirements](/run-a-node/requirements#hardware). + + + Your choice, and larger is better for hit rate. Our test nodes run 250 GB to + 1 TB. If your hit rate is low on testnet, increase the cache first. See + [Disk and cache size](/run-a-node/requirements#disk-and-cache-size). + + + Yes. Residential and homelab nodes are fully supported, and an unmetered + home connection is the cheapest way to run one. + + + +## Bond and earnings + + + + The bond scales with the capacity you declare. On testnet it is 50,000 TOKEN + for any declaration up to about 1 Gbps. `decdn setup --dry-run` prints the + exact figure. See [Bond and + earnings](/run-a-node/bond-and-earnings#the-capacity-bond). + + + Only rate manipulation and serving blacklisted content after its compliance + window. Bad delivery is unpaid, not slashed. See [Compliance and + risk](/run-a-node/compliance#what-can-cost-you-bond). + + + 14 days, during which the bond stays slashable. See + [Unbonding](/run-a-node/bond-and-earnings#unbonding). + + + Per byte delivered, in USDC. The node redeems its vouchers on-chain + automatically and the operator share goes straight to your address. See [How + you earn](/run-a-node/bond-and-earnings#how-you-earn). + + + The target client price is about $0.01/GB, and each operator sets its own + per-MB rate. Mainnet launches at 90% operator, 0% buyback, 10% treasury, and + moves to 60/30/10 once governance confirms the activation criteria. The + testnet already runs 60/30/10. See [Operator + share](/run-a-node/bond-and-earnings#operator-share). + + + +## Traffic + + + + Yes, from real clients. There is no synthetic traffic, so volume reflects + demand near you. See [What to expect on + testnet](/run-a-node/traffic#what-to-expect-on-testnet). + + + Clients pick the responsive node with the lowest round-trip time. Nodes + filling a cache miss weigh price, latency and reputation. See [How a request + reaches you](/run-a-node/traffic#how-a-request-reaches-you). + + + Yes. Clients in your region reach you fastest, and regional warming routes + their requests through your node so it caches what they pull. Turn on + pull-through to take part. See [Regional + warming](/run-a-node/traffic#regional-warming). + + + +## Compliance + + + + You follow the law where you operate and accept the operator terms at registration. The node follows the on-chain blacklist on its own, and you can refuse any content locally. See [Compliance and risk](/run-a-node/compliance). + + + Yes. Evict it with `decdn node evict `, deny it in the `[content]` config section, or stop the daemon. See [Refusing content yourself](/run-a-node/compliance#refusing-content-yourself). + + + +## Mainnet + + + + No hardware minimum is planned. The bond keeps scaling with declared + capacity, and selection keeps rewarding fast, reliable nodes. + + + The target is Q1 2027. The date is fixed once the testnet has earned it. See + [the road to testnet](https://decdn.org/blog/road-to-testnet/). + + diff --git a/docs/run-a-node/operating.mdx b/docs/run-a-node/operating.mdx new file mode 100644 index 0000000..8838b06 --- /dev/null +++ b/docs/run-a-node/operating.mdx @@ -0,0 +1,73 @@ +--- +title: Operating a node +description: Day-to-day operation of a deCDN node: status commands, cache sizing and hit rate, pull-through, disk, keystore, and changing region or capacity. +--- + +## Status commands + +`decdn node` talks to the running daemon over its localhost admin port. + +| Command | Shows | +| ------------------------- | -------------------------------------------------------------- | +| `decdn node health` | Node identity and uptime | +| `decdn node status` | DHT participation | +| `decdn node top` | Live metrics | +| `decdn node lanes` | USDC accrued per payment lane, against the redeem threshold | +| `decdn node pools` | The node's buyer pool for cache-miss pulls | +| `decdn node slashes` | Any slash against this operator, with its appeal deadline | +| `decdn node doctor` | Readiness checks across config, chain and network | +| `decdn node reload` | Re-reads the hot-reloadable config sections (same as `SIGHUP`) | +| `decdn node drain --wait` | Graceful shutdown that finishes in-flight deliveries | + +The daemon also exposes Prometheus metrics at `http://127.0.0.1:9090/metrics`. + +## Cache hit rate + +The hit rate is the share of requests your node serves from its own disk. Read it from the serving-path counters: + +```text +hit rate = decdn_serve_cache_hit_total + / (decdn_serve_cache_hit_total + decdn_serve_cache_partial_hit_total + decdn_serve_cache_miss_total) +``` + +Use the `decdn_serve_cache_*` counters. The `decdn_cache_hits_total` family counts a different path and reads 0% on a serving node. + +**If the hit rate is low, increase the cache first.** Raise `cache.cache_size_mb` toward the free disk you can spare and restart the node. `decdn_cache_bytes` against `decdn_cache_size_limit_bytes` shows how full the cache is, and a high `decdn_cache_evictions_total` means the cache is too small for the demand it sees. Every miss you avoid is a paid upstream pull you do not make. + +## Pull-through + +With pull-through off, a cache miss is served as not-found and the client goes elsewhere. With pull-through on, the node buys the missing blob from another node, streams it to the client and caches it. This is what lets a node fill its cache from demand and take part in [regional warming](/run-a-node/traffic#regional-warming). + +```toml ~/.decdn/node.toml +[cache] +node_to_node_pull_through_enabled = true +``` + +Pull-through spends USDC from the node's buyer pool, 10 USDC by default. Fund the operator address with testnet USDC from [Circle's faucet](https://developers.circle.com/stablecoins/docs/usdc-on-testnet). The node opens the pool on its own. If pulls never succeed, check `decdn_buyer_wallet_usdc` and `decdn node pools`. To cap what the node pays per MB upstream, set `cache.max_rate_per_mb`. + +## Disk + +The cache limit is an upper bound. The node evicts least-recently-used blobs above 90% of the limit, and always keeps `cache.disk_headroom_mb` (8 GB by default) free on the volume. If the disk fills anyway, **lower** `cache_size_mb` or add disk. Never delete or truncate blob files by hand: use `decdn node evict ` to remove one blob. + +## Keystore password + +The daemon reads the keystore password from `DECDN_KEYSTORE_PASSWORD`, then from the file named by `--keystore-password-file`, then from an interactive prompt. A headless host needs one of the first two. Keep the password file at mode `0600`, owned by the node's user. + +## Config changes + +`decdn node reload` or `SIGHUP` applies the log level, pinned hashes, and the `[security]`, `[content]` and `[load_shed]` sections without a restart. Every other change, including `rate_per_mb` and `cache_size_mb`, needs a restart. A restart drains in-flight deliveries first. + +## Changing capacity, region or address + +| Change | Command | +| --------------------------- | ------------------------------------------------- | +| Raise declared capacity | `decdn node bond --mbps ` | +| Lower declared capacity | `decdn node unbond --to-mbps ` | +| New public IP or port | `decdn node update-multiaddrs --multiaddr ` | +| Move to a different country | `decdn node update-region --region ` | + +A region change is allowed once per 7-day stability window, which starts at registration. During the window, the node stays bound by the previous region's blacklist entries. + +## Upgrading + +Install the latest release over the old binaries, then restart the daemon. The restart drains in-flight work, so upgrades do not cut paid deliveries short. The full operator runbook, covering RPC outages, keystore problems and blacklist incidents, is in the [decdn repo](https://github.com/decdn/decdn/blob/main/docs/runbook.md). diff --git a/docs/run-a-node/overview.mdx b/docs/run-a-node/overview.mdx new file mode 100644 index 0000000..a40bc32 --- /dev/null +++ b/docs/run-a-node/overview.mdx @@ -0,0 +1,41 @@ +--- +title: Run a node +description: What a deCDN node does, who runs one, and what to expect from the public testnet on Arbitrum Sepolia. +--- + +A deCDN node caches content-addressed blobs and serves them to clients over QUIC. Clients pay the node per megabyte in USDC, and the node bonds TOKEN in proportion to the bandwidth it declares. Anyone can run one: onboarding is permissionless, and a single `decdn setup` command bonds and registers the node on-chain. + +## The public testnet + +The testnet runs on **Arbitrum Sepolia** (chain ID 421614). It carries real client demand, mostly AI model weights pulled through the walletless on-ramp. There is no synthetic traffic, so the volume a node sees reflects real demand near it. + +Bonding uses testnet TOKEN, which the team sends to operators on request. See [Get testnet funds](/run-a-node/testnet-funds). + +## Who runs a node + +- **Home and homelab operators.** A residential connection with unmetered bandwidth is the cheapest way to run a node, and it is fully supported. A static IP is not required. +- **Server and datacenter operators.** The same binary runs on a VPS or bare metal. Bond size scales with the capacity you declare, so a larger link bonds more. +- **Operators in under-served regions.** Clients pick the fastest node they can reach, so a node in a region with few holders gets local demand that distant nodes serve slowly. + +## The path + + + + Hardware, disk, the one UDP port to open, and how NAT is handled. See + [Requirements](/run-a-node/requirements). + + + Arbitrum Sepolia ETH for gas, TOKEN for the bond, and optionally USDC for + cache-miss pulls. See [Get testnet funds](/run-a-node/testnet-funds). + + + Install the binaries, write the config, preview with `--dry-run`, then bond + and register. See [Quickstart](/run-a-node/quickstart). + + + Size the cache, watch the hit rate, and keep the node healthy. See + [Operating a node](/run-a-node/operating). + + + +Before you start, read [Compliance and risk](/run-a-node/compliance). Your node serves content from your own IP address, and you accept the operator terms at registration. diff --git a/docs/run-a-node/quickstart.mdx b/docs/run-a-node/quickstart.mdx new file mode 100644 index 0000000..adf03cf --- /dev/null +++ b/docs/run-a-node/quickstart.mdx @@ -0,0 +1,128 @@ +--- +title: Quickstart +description: Install the deCDN node, write its config, bond and register on Arbitrum Sepolia, and run it under systemd or Docker. +--- + +deCDN ships two binaries. `decdn` is the CLI you type commands into: setup, bonding, status. `decdn-node` is the daemon that serves traffic. You need both on the host. + +## 1. Install + +Pick one of three ways. + +**Release archive.** Download the `decdn` and `decdn-node` archives for your platform from the [latest release](https://github.com/decdn/decdn/releases/latest), then put both binaries on your `PATH`. A maintainer signs the `SHA256SUMS` manifest that covers the archives; [SECURITY.md](https://github.com/decdn/decdn/blob/main/SECURITY.md) explains how to verify it. + +**Cargo.** + +```bash +cargo install --locked decdn-cli decdn-node +``` + +**Docker.** The image holds the `decdn-node` daemon only, so install the `decdn` CLI from the release archive or with Cargo as well. You use it for keys, setup and status. + +```bash +docker pull ghcr.io/decdn/decdn-node:latest +``` + +## 2. Write the config + +```bash +decdn config init +``` + +This writes `~/.decdn/node.toml` with the testnet RPC endpoint and the deployed contract addresses filled in. Open it and set the cache size to match the disk you give the node: + +```toml ~/.decdn/node.toml +[cache] +cache_size_mb = 512000 # 500 GB +``` + +See [Requirements](/run-a-node/requirements#disk-and-cache-size) for how to size it. The default RPC endpoint is the public, rate-limited Arbitrum Sepolia endpoint. For a node that runs 24/7, use an RPC provider of your own. + +## 3. Create keys and get funds + +```bash +decdn key-gen +decdn whoami +``` + +Send the operator address that `whoami` prints to the team to receive testnet TOKEN, and fund it with a little Arbitrum Sepolia ETH. See [Get testnet funds](/run-a-node/testnet-funds). + +## 4. Bond and register + +Preview first. `--dry-run` checks the RPC, your clock, your TOKEN and ETH balances and the exact bond, and submits nothing: + +```bash +decdn setup --mbps 250 --region BR \ + --multiaddr /ip4//udp/4433/quic-v1 \ + --dry-run +``` + +When every check passes, run it for real: + +```bash +decdn setup --mbps 250 --region BR \ + --multiaddr /ip4//udp/4433/quic-v1 +``` + +`setup` shows the [operator terms](https://github.com/decdn/decdn/blob/main/crates/cli/TERMS.md) and asks you to accept them, confirms the bond, then bonds, declares your capacity and registers the node on-chain. It ends with a go/no-go summary. + +- `--mbps` is the bandwidth you commit to serve. It sets your bond. Declare what your uplink sustains. +- `--region` is your ISO 3166-1 alpha-2 country code, such as `BR` or `DE`. It sets which regional takedown rules apply to you. See [Compliance and risk](/run-a-node/compliance). +- `--multiaddr` is the public address peers dial. A dual-stack host passes a second one: `/ip6//udp/4433/quic-v1`. Omit the flag to register the host's detected public address. A host behind NAT with no forwarded port registers none and relies on hole-punching and relays. + +## 5. Run the node + +The daemon needs the keystore password to start. On a headless host, put it in a file readable only by the node's user and pass `--keystore-password-file`, or set `DECDN_KEYSTORE_PASSWORD`. + +### Under systemd + +```ini /etc/systemd/system/decdn-node.service +[Unit] +Description=deCDN node +After=network-online.target +Wants=network-online.target + +[Service] +User=decdn +ExecStart=/usr/local/bin/decdn-node run \ + --config /home/decdn/.decdn/node.toml \ + --keystore-password-file /etc/decdn/keystore-password +ExecReload=/bin/kill -HUP $MAINPID +KillSignal=SIGTERM +TimeoutStopSec=300 +Restart=always +RestartSec=5 + +[Install] +WantedBy=multi-user.target +``` + +```bash +sudo systemctl daemon-reload +sudo systemctl enable --now decdn-node +``` + +`SIGTERM` drains in-flight deliveries and flushes settlement before the process exits. Keep `TimeoutStopSec` generous so a stop never cuts a paid delivery short. + +### Under Docker + +```bash +docker run -d --name decdn-node --restart unless-stopped \ + -p 4433:4433/udp \ + -v ~/.decdn:/home/decdn/.decdn \ + -e DECDN_KEYSTORE_PASSWORD_FILE=/home/decdn/.decdn/keystore-password \ + ghcr.io/decdn/decdn-node:latest +``` + +Publish 4433 as **UDP**: QUIC does not use TCP. The volume holds the config, keys and cache, so mount it on the disk you sized the cache for. The container runs as uid 1000, so that user must own the mounted directory. + +## 6. Check it + +```bash +decdn node health # identity and uptime +decdn node status # DHT participation +decdn node doctor # end-to-end readiness checks +decdn node top # live metrics +``` + +Your node earns from the first paid delivery onward. Next, turn on [pull-through](/run-a-node/operating#pull-through) so it can fill cache misses, and read [How you get traffic](/run-a-node/traffic) to know what volume to expect. diff --git a/docs/run-a-node/requirements.mdx b/docs/run-a-node/requirements.mdx new file mode 100644 index 0000000..b6124fb --- /dev/null +++ b/docs/run-a-node/requirements.mdx @@ -0,0 +1,48 @@ +--- +title: Requirements +description: Hardware, disk, network and operating-system requirements for a deCDN node, including the one UDP port to open. +--- + +## Hardware + +The protocol enforces no hardware minimum. The recommended starting spec from the [onboarding design](https://github.com/decdn/decdn/blob/main/adr/019-node-onboarding.md) is: + +| Resource | Recommended | +| -------- | ------------------- | +| CPU | 4 vCPU | +| Memory | 8 GB RAM | +| Disk | SSD, sized to cache | +| Egress | 5 TB/month | + +Content is verified with BLAKE3 as it streams, which runs at several GB/s on modern CPUs, so a recent desktop CPU is not the bottleneck. The node also needs a clock synchronized with NTP: `decdn setup` refuses to bond when the local clock is more than 10 seconds off UTC. + +## Disk and cache size + +Cache size is your choice, and it is the setting that most affects how often your node can serve a request from local disk. A larger cache holds more of the catalogue, so more requests are cache hits. Testnet content is mostly model weights, from a few GB to more than 40 GB per model. + +Our own test nodes run caches of **250 GB to 1 TB**. The node's default cache limit is 100 GB (`cache.cache_size_mb = 102400`), so raise it to match the disk you give the node. The node keeps 8 GB of the volume free at all times (`cache.disk_headroom_mb`). + +The trade-off is economic. A cache miss either goes unserved or is filled by a paid pull from another node, and that pull costs USDC. A larger cache means fewer paid pulls. If your hit rate on testnet is low, increase the cache first. See [Operating a node](/run-a-node/operating#cache-hit-rate). + +## Network + +| Port | Protocol | Exposure | Purpose | +| -------- | -------- | ------------------------- | ----------------------------------- | +| **4433** | UDP | Public (IPv4 and IPv6) | QUIC: probes, delivery, DHT | +| 9090 | TCP | Localhost by default | Prometheus `/metrics` | +| 9191 | TCP | Localhost only (enforced) | Admin JSON-RPC used by `decdn node` | + +Open **UDP 4433** to the internet, and forward it on your router if you are behind NAT. Keep metrics and admin on localhost. The node makes outbound connections to the Arbitrum RPC endpoint and to relays. + +**A static IP is not required.** The transport hole-punches through NAT, falls back to a content-blind relay when a direct path fails, and updates the node's registered addresses when its public address changes. A direct UDP path is faster than a relayed one, so a stable public IPv4 or IPv6 address with 4433 forwarded gives the best results. See [NAT traversal](/protocol/network#nat-traversal). + +## Operating system + +| Platform | Status | +| --------------------------- | ---------------------------------------------- | +| Linux (x86_64, aarch64) | Recommended. Our own fleet runs under systemd. | +| Docker (linux/amd64, arm64) | Supported. The repo ships a `Dockerfile`. | +| macOS (x86_64, aarch64) | Builds and runs, suited to testing. | +| Windows (x86_64) | Builds and runs, suited to testing. | + +For a node that runs 24/7, use Linux with systemd or Docker. See [Quickstart](/run-a-node/quickstart). diff --git a/docs/run-a-node/testnet-funds.mdx b/docs/run-a-node/testnet-funds.mdx new file mode 100644 index 0000000..b155a49 --- /dev/null +++ b/docs/run-a-node/testnet-funds.mdx @@ -0,0 +1,43 @@ +--- +title: Get testnet funds +description: How to get Arbitrum Sepolia ETH for gas, TOKEN for the capacity bond, and USDC for cache-miss pulls on the deCDN testnet. +--- + +A testnet node needs up to three assets on Arbitrum Sepolia, all held by the node's operator address. + +| Asset | Used for | How to get it | +| --------- | --------------------------------------- | ----------------------------------------------------------------------------------------- | +| **TOKEN** | The capacity bond | The deCDN team sends it. See below. | +| **ETH** | Gas for bonding, registering, redeeming | Any Arbitrum Sepolia faucet | +| **USDC** | Paying peers for cache-miss pulls | [Circle's testnet faucet](https://developers.circle.com/stablecoins/docs/usdc-on-testnet) | + +## Your operator address + +Your operator address is the Ethereum address of the node's keystore. Create the keys, then print the address: + +```bash +decdn key-gen # writes the node key and an encrypted Ethereum keystore to ~/.decdn +decdn whoami # prints the node ID and the operator address +``` + +`key-gen` asks for a keystore password. Keep it: the daemon needs it to start. `decdn setup` reuses these keys. + +## TOKEN + +The team sends testnet TOKEN to operators directly. Send your **EVM operator address** through any channel on the [contact section](https://decdn.org/#contact): + +- email [info@decdn.org](mailto:info@decdn.org) +- the [Discord](https://discord.gg/vVbNswKVGX) +- [X](https://x.com/decdn_) or [GitHub](https://github.com/decdn/decdn/issues) + +Say which capacity you plan to declare. The testnet bond is **50,000 TOKEN** for any capacity up to about 1 Gbps. See [Bond and earnings](/run-a-node/bond-and-earnings). + +## ETH for gas + +Bonding and registering together take about 600,000 gas, and `decdn setup` checks the balance before it submits anything. After that, the node spends gas to redeem its earnings on-chain. A small amount of Arbitrum Sepolia ETH covers a long time. + +## USDC for cache-miss pulls + +USDC is optional at first. A node only spends it when **node-to-node pull-through** is on. With pull-through on, the node fills a cache miss by buying the blob from another node, then serves and caches it. The node keeps a buyer pool of 10 USDC for this by default (`blockchain.buyer_working_deposit_micro_usdc`). + +Pull-through is off by default. Turn it on once the node is running, so that your node can fill misses and take part in [regional warming](/run-a-node/traffic#regional-warming). See [Operating a node](/run-a-node/operating#pull-through). diff --git a/docs/run-a-node/traffic.mdx b/docs/run-a-node/traffic.mdx new file mode 100644 index 0000000..021fdd5 --- /dev/null +++ b/docs/run-a-node/traffic.mdx @@ -0,0 +1,29 @@ +--- +title: How you get traffic +description: How clients and nodes find and select your node, how a first node in a new region warms its cache, and what volume to expect on testnet. +--- + +## How a request reaches you + +1. **Discovery.** Every node publishes a DHT record for each blob it caches. A client or node looking for a hash finds the holders through the DHT. The bonded set itself comes from the on-chain registry. +2. **Probing.** The requester probes candidates in parallel. Each probe response confirms availability, measures round-trip time and carries your signed per-MB rate. +3. **Selection.** + - **Clients** rank candidates by measured round-trip time only. The closest responsive node wins, as long as its rate is under the client's ceiling. + - **Nodes** filling a cache miss rank holders by a combined score of price, latency and their local reputation of each holder. They collect probe answers for up to 500 ms, and stop early once enough viable holders answer. + +The region you declare does not partition the network. A client uses regions only to shortlist which nodes to probe, then picks on measured latency. A region claim that latency contradicts costs the node reputation. See [Network topology](/protocol/network). + +## Regional warming + +A first node in a new region starts with an empty cache. Regional proxy warming fills it from real demand: + +1. A client finds that every holder of a blob is far away, and that a bonded node it can reach is much closer. +2. The client sends its paid request through that nearby node, even though the node does not hold the blob. +3. The nearby node pulls the blob from a distant holder, streams it to the client as it arrives, and keeps the copy. +4. It announces the copy in the DHT. Later requests from the region go to it directly, because it answers fastest. + +Warming needs a cache-only node with [pull-through](/run-a-node/operating#pull-through) turned on and a funded buyer pool, because the node pays the distant holder for the pull. It is on by default in clients. See [ADR 037](https://github.com/decdn/decdn/blob/main/adr/037-regional-proxy-warming.md). + +## What to expect on testnet + +Testnet traffic is real client demand, mostly model downloads, and today most clients are in North America and Europe. There is no synthetic traffic. A node in a region with few clients sees modest volume at first, and that volume is an honest measure of local demand. Volume grows with the clients near you, and a node that is the first in its region is the one that warms up when they arrive. From beb29d7b303a6660db21b4db85f9d8b48ef6e39a Mon Sep 17 00:00:00 2001 From: Alper Gundogdu Date: Fri, 9 Oct 2026 11:10:48 +0100 Subject: [PATCH 2/3] docs(run-a-node): link the public stats site Co-Authored-By: Claude Opus 5.5 --- docs/run-a-node/operating.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/run-a-node/operating.mdx b/docs/run-a-node/operating.mdx index 8838b06..ba3827d 100644 --- a/docs/run-a-node/operating.mdx +++ b/docs/run-a-node/operating.mdx @@ -19,7 +19,7 @@ description: Day-to-day operation of a deCDN node: status commands, cache sizing | `decdn node reload` | Re-reads the hot-reloadable config sections (same as `SIGHUP`) | | `decdn node drain --wait` | Graceful shutdown that finishes in-flight deliveries | -The daemon also exposes Prometheus metrics at `http://127.0.0.1:9090/metrics`. +The daemon also exposes Prometheus metrics at `http://127.0.0.1:9090/metrics`. Network-wide figures, including registered nodes and settlements, are public at [stats.decdn.org](https://stats.decdn.org). ## Cache hit rate From 115dc5f641bd6bbb2dbf37ed003bb6420f89863c Mon Sep 17 00:00:00 2001 From: Alper Gundogdu Date: Fri, 9 Oct 2026 11:19:48 +0100 Subject: [PATCH 3/3] docs(run-a-node): document DECDN_KEYSTORE_PASSWORD_FILE Co-Authored-By: Claude Opus 5.5 --- docs/run-a-node/operating.mdx | 2 +- docs/run-a-node/quickstart.mdx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/run-a-node/operating.mdx b/docs/run-a-node/operating.mdx index ba3827d..63606d1 100644 --- a/docs/run-a-node/operating.mdx +++ b/docs/run-a-node/operating.mdx @@ -51,7 +51,7 @@ The cache limit is an upper bound. The node evicts least-recently-used blobs abo ## Keystore password -The daemon reads the keystore password from `DECDN_KEYSTORE_PASSWORD`, then from the file named by `--keystore-password-file`, then from an interactive prompt. A headless host needs one of the first two. Keep the password file at mode `0600`, owned by the node's user. +The daemon reads the keystore password from `DECDN_KEYSTORE_PASSWORD`, then from the file named by `--keystore-password-file` (or its environment form, `DECDN_KEYSTORE_PASSWORD_FILE`), then from an interactive prompt. A headless host needs one of the first two. Keep the password file at mode `0600`, owned by the node's user. ## Config changes diff --git a/docs/run-a-node/quickstart.mdx b/docs/run-a-node/quickstart.mdx index adf03cf..a9ac1d7 100644 --- a/docs/run-a-node/quickstart.mdx +++ b/docs/run-a-node/quickstart.mdx @@ -72,7 +72,7 @@ decdn setup --mbps 250 --region BR \ ## 5. Run the node -The daemon needs the keystore password to start. On a headless host, put it in a file readable only by the node's user and pass `--keystore-password-file`, or set `DECDN_KEYSTORE_PASSWORD`. +The daemon needs the keystore password to start. On a headless host, put it in a file readable only by the node's user and pass `--keystore-password-file` (or set `DECDN_KEYSTORE_PASSWORD_FILE`, as the Docker example does), or set `DECDN_KEYSTORE_PASSWORD`. ### Under systemd