Pewarta Proxy

πŸ“‹ Server Request & Proxy Logs

Loading active proxy settings...
Developed by Khairul Adha (adhexa.id)
Mengambil log server...

βš™οΈ Pengaturan Proxy Target

Skip to content
decdnPublic

About

Decentralized CDN where nodes cache and serve content-addressed blobs over iroh QUIC, and clients pay per-MB via off-chain USDC shared payment pools.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

46 stars

Watchers

0 watching

Forks

Latest commit

Β 

History

1,711 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

deCDN

A decentralized CDN. Anyone who bonds can serve bytes; clients pay per megabyte.

Bonded operators cache and serve BLAKE3-addressed content over iroh QUIC. Clients pay for every megabyte in USDC and verify every byte against its content hash, so any node can serve any blob without being trusted.

decdn get pulling content from the deCDN testnet

CI Security CodeQL Release crates.io docs.rs Docker MSRV License: MIT OR Apache-2.0 Discord

Website Β· Docs Β· Network stats Β· Discord Β· X

⚑ Try it in one line

Download Qwen2.5 0.5B Instruct, a 491 MB GGUF that runs in llama.cpp or ollama, from the testnet. It takes about a minute on a 100 Mbps line. On macOS or Linux:

curl -fsSL https://up.decdn.org/decdn.sh | sh -s -- get 'decdn:d0a3eac609db6f49de12213574a302e42afd45f15b2cd89aa46b8a3e91f74d13/?ns=1'

On Windows, in PowerShell:

& ([scriptblock]::Create((irm https://up.decdn.org/decdn.ps1))) get 'decdn:d0a3eac609db6f49de12213574a302e42afd45f15b2cd89aa46b8a3e91f74d13/?ns=1'

The file's SHA-256 is 74a4da8c9fdbcd15bd1f6d01d621410d31c6fc00986f5eb687824e7b93d7a9db, the one Hugging Face publishes. For a bigger pull, Mistral 7B Instruct v0.3 is about 29 GB; either line takes its URI in place of the one above:

curl -fsSL https://up.decdn.org/decdn.sh | sh -s -- get 'decdn:9194000d7b356650e6924a7746ec4afb0b705838b65913c0e46cba6b15af69e5/?ns=1'

The line installs decdn and asks you to pass one check in the browser (it opens the link, or prints it). Then it downloads the bundle into its own ./<hash>/ directory and verifies every byte on the way in. It refuses to overwrite a file it did not write itself; add --overwrite after the URI to replace one. Run it again after an interruption and it resumes. A sponsor pays for the transfer, so you need no wallet, keys, or account. Later, decdn self-update moves the installed binary to the latest signed release.

The catalogue on decdn.org lists more seeded content. To pay for your own users' downloads the same way, run sponsord, the sponsorship daemon and onramp that ship with every release.

✨ Features

  • Verified bytes, untrusted nodes. Content is BLAKE3-addressed and the hash is a Merkle root, so any byte range verifies on its own, which makes resume and multi-source fetch safe (ADR 002, ADR 038).
  • Pay per megabyte. A client funds one shared PaymentPool in USDC and pays each node with cumulative off-chain vouchers (ADR 003).
  • Anyone can serve. Operators bond TOKEN against their declared bandwidth, register on-chain, then cache and serve content (ADR 001, ADR 026).
  • Multi-source fetch. A large fetch spreads disjoint byte ranges across several holders and reassigns ranges as sources finish or stall (ADR 039).
  • Private origins. S3, R2, B2, HTTP, and filesystem backends are per-node config. No origin URL is ever exposed (ADR 002).
  • Enforced on-chain. Every quote and stream response carries a signature that serves as on-chain slash evidence, a slashed operator has 30 days to appeal, and governance blacklists content hashes (ADR 014, ADR 028, ADR 011).
  • Governed by service. No TOKEN balance carries a vote. Vote weight is trailing-window served bytes (ADR 036).

🧭 Status

Public testnet on Arbitrum Sepolia; production targets Arbitrum One. The contracts ship at full production shape, with governance-tunable economics from day one (ADR 016). The implementation is Rust. Signed release archives and container images ship with each release, and crates publish to crates.io.

Wire, ABI, config, and storage formats change without compatibility shims until the mainnet deployment. Many ADRs are still marked Draft, so the specification can change too.

πŸ“¦ Install

cargo binstall --locked decdn-cli decdn-node   # needs cargo-binstall; installs the release archive
cargo install --locked decdn-cli decdn-node    # or build from source
docker pull ghcr.io/decdn/decdn-node     # daemon + CLI; mirrored to docker.io/decdn/decdn-node
docker pull ghcr.io/decdn/decdn          # CLI only; mirrored to docker.io/decdn/decdn
docker pull ghcr.io/decdn/sponsord       # sponsorship daemon, for publishers who pay for their users' downloads
docker pull ghcr.io/decdn/sponsord-onramp # its public onramp; both mirrored to docker.io/decdn/…

Each GitHub release also carries decdn-${VERSION}-${TARGET} and decdn-node-${VERSION}-${TARGET} archives (.tar.gz, or .zip on Windows), and sponsord-… and sponsord-onramp-… archives for Linux. SECURITY.md explains how to verify each channel.

A decdn installed from an archive or by the one-liner updates itself: decdn self-update fetches the latest release, verifies its SHA256SUMS.minisig against a maintainer key the binary embeds, and replaces the binary in place. A binary in a cargo bin directory (cargo install, cargo binstall), or one in a directory you cannot write (the container image, a package), is refused with the command to run instead. Once a day, after a command succeeds with the terminal as its output, decdn prints one line when a newer release exists; [self_update] check = false in node.toml or DECDN_SELF_UPDATE_CHECK=0 turns that off. decdn-node never updates itself.

Running the container images

The decdn-node image runs the daemon and also carries the CLI, so docker exec <container> decdn node status reaches the daemon's local admin port. The decdn image runs the CLI. Mount your key directory and a working directory:

docker run --rm -it -v ~/.decdn:/home/decdn/.decdn -v "$PWD":/work -w /work \
  ghcr.io/decdn/decdn get decdn:<hash> -o blob.bin

Both images run as uid 1000. If your user has another uid, add --user "$(id -u):$(id -g)" -e HOME=/home/decdn. Without a TTY, mount a file that holds the keystore password and point DECDN_KEYSTORE_PASSWORD_FILE at it.

A maintainer signs the SHA256SUMS manifest that covers the release archives, and each container image's digest file and SBOM. crates.io artifacts carry no maintainer signature.

Build from source

You need the Rust toolchain that rust-toolchain.toml pins (rustup installs it automatically), a C compiler, and make.

git clone --recurse-submodules https://github.com/decdn/decdn.git
cd decdn
cargo build --release -p decdn-cli -p decdn-node
# Binaries: target/release/decdn and target/release/decdn-node

CONTRIBUTING.md lists the full development prerequisites, including Foundry for the contracts.

πŸš€ Quick start

Fetch with your own wallet

A client uses its own keystore under ~/.decdn/client, separate from any node identity on the same host. Fund the address that key-gen prints with Arbitrum Sepolia ETH and USDC.

decdn config init --client --output ~/.decdn/client.toml
decdn key-gen --output-dir ~/.decdn/client
decdn --config ~/.decdn/client.toml quote decdn:<blake3-hash>
decdn --config ~/.decdn/client.toml get decdn:<blake3-hash> -o ./blob.bin --receipt ./blob.receipt.json
decdn receipt verify ./blob.receipt.json

quote prices the pull from the rates the nodes sign in their probe answers, and pays nothing for a blob; a bundle with no cached manifest costs one small paid pull for it, from your open pool. With no open pool, quote stops before it opens one: add --open-pool to let it, which escrows the working deposit (10 USDC by default) and pays gas, or fund a pool first with decdn pool open. With or without the flag, an open pool that runs low gets a top-up, which also escrows USDC and pays gas. get prints the same estimate on one line before it starts. It ends with a receipt: the bytes it verified, the nodes that served them, and the USDC it paid. --receipt writes the nodes' signed answers and your vouchers to a file that decdn receipt verify checks offline and decdn receipt settle updates once the nodes redeem (appendix).

get pays each node it uses from a pool you own. It reuses your existing pool or opens one with a 10 USDC working deposit, and it tops the pool up on-chain without prompting when the balance runs low. Fund the wallet with only what you are willing to spend.

decdn: URIs, node selection, and overwrite rules

A decdn: URI names a blob, a whole bundle, or one file of a bundle (appendix):

URI get pulls -o is Without -o
decdn:<hash> the blob a file, or - for stdout ./<hash>
decdn:<hash>/ every file of the bundle whose manifest has this hash a directory ./<hash>/
decdn:<hash>/a/b.txt that one file of the bundle a directory, or - for stdout ./b.txt
any of these with ?ns=<id> the same, routed to a namespace's origins the same the same

Without -o, a bundle or entry pull refuses to replace a file unless an earlier pull wrote it there and it has not changed since. That earlier pull must be of the same entry, or have written the same bytes. It also refuses a hidden path β€” one with a .-prefixed component β€” outright; name the directory with -o <dir> to pull such paths. A new version of a bundle has a new hash, so it lands in its own directory; to update the old directory in place, pass -o <the old bundle's directory>: the pull replaces the files that changed and skips the rest. --overwrite fetches every file again.

get probes bonded nodes and picks one that holds the blob. When none does, it tries bonded non-holders nearest first. Each fills the miss from its own origin, or from a peer if it has pull-through on. A node with neither answers not-found, and get moves to the next. One pool pays every provider, so there is no per-provider setup. Use decdn pool {…} to inspect, close, or reclaim a pool.

Run a node

You need Arbitrum Sepolia ETH for gas, TOKEN for the bond, and a keystore password source for the headless daemon. The operator guide covers testnet funds, the bond (ADR 026 Β§ Capacity-bond curve), and the full quickstart.

decdn config init            # writes ~/.decdn/node.toml with the testnet RPC and contract addresses
decdn setup --mbps 100 --region US --multiaddr /ip4/203.0.113.10/udp/4433/quic-v1
decdn-node run
decdn node health            # then: node status, node lanes, node earnings, node top

setup generates the node key and keystore when neither exists, shows the operator terms, bonds, registers the node, and prints a go/no-go summary (ADR 019).

Bond size, pull-through, IPv6, and serving your own content
  • Bond. The bond is max(minBond, bondRequired(mbps)), where bondRequired is a curve over declared bandwidth, super-linear at the default exponent (ADR 026 Β§ Capacity-bond curve, Β§ Minimum bond). The testnet deployed with minBond at 50,000 TOKEN (manifest), which at the default curve (k = 12.6, Ξ± = 1.2) covers declarations up to about 997 Mbps; 1,000 Mbps needs about 50,162 TOKEN. decdn node bond --mbps <n> --dry-run prints the live target (it unlocks the keystore first). The maintainers send testnet TOKEN: send your EVM operator address (decdn whoami prints it) through any channel on the contact page.
  • USDC. The node's own buyer pool pays for cache-miss pulls from peers (10 USDC working deposit by default). The node spends it only with cache.node_to_node_pull_through_enabled on, which is the default for a node with no origin backend and off for an origin node. decdn node status shows the setting and the buyer wallet's balance. With it off, or with no USDC to open the pool, a cache miss the node cannot serve from its own backend returns not-found (runbook). Circle's testnet faucet dispenses it.
  • Keystore password. Set DECDN_KEYSTORE_PASSWORD or pass --keystore-password-file (runbook).
  • Network. The node listens on UDP 4433 over IPv4 and IPv6, so a dual-stack host also passes --multiaddr /ip6/<addr>/udp/4433/quic-v1. --region is an ISO 3166-1 alpha-2 country code (ADR 030).
  • Your own content. Import it into a local origin store with decdn origin import --input <path> --to <dir>, then write the node config with decdn config init --origin file://<absolute-dir> (--force replaces an existing one). For an S3 origin, import to a local directory and aws s3 sync it to the bucket: the two layouts are identical.

Publish content

A publisher creates a namespace and seats the bonded operators that serve as its origins. publish moves no bytes. A client passes the namespace with get decdn:<hash>?ns=<id>, and a node that misses the blob pulls it from one of the seated operators' origin backends (ADR 005 Β§ Namespace routing).

decdn publish namespace create                     # prints the namespace id
decdn publish assign <namespace-id> <0xOPERATOR>...

The installed vetting policy decides who may seat origins (ADR 011 Β§ Vetting policies). The testnet runs the manual policy, so a VETTER_ROLE holder must vet the publisher's address before assign succeeds. That step happens out-of-band, not through the CLI.

Local devnet

To run without testnet funds, contracts/dev-deploy.sh starts Anvil and deploys the full protocol, a mock USDC, and a TOKEN faucet. See CONTRIBUTING.md Β§ Local deployment.

The decdn-e2e tests are runnable end-to-end journeys: onboarding, paid fetch, and publishing. They need cargo-nextest and Foundry, and they drive the debug binaries, so build those first:

cargo build -p decdn-cli -p decdn-node
cargo nextest run -p decdn-e2e --features anvil-e2e

πŸ—οΈ How it works

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ on-chain (Arbitrum) ────────────────────┐
β”‚  CapacityBond   bonded TOKEN Β· registry of active nodes    β”‚
β”‚  PaymentPool    USDC deposits Β· voucher redemption         β”‚
β””β”€β”€β”€β”€β”€β–²β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–²β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–²β”€β”€β”€β”€β”€β”€β”€β”€β”˜
      β”‚ deposit Β·        β”‚ bond Β· deposit Β· redeem  β”‚ bond Β· redeem
      β”‚ close Β· reclaim  β”‚                          β”‚
β”Œβ”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   Client   │◄──►│  Cache node  │◄──►│  Origin node  │◄──── S3/R2/B2/HTTP/fs β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
    ◄─► bytes one way, USDC vouchers the other (cdn/client/v1)
  • Nodes bond TOKEN against their declared bandwidth, register on-chain, then cache and serve content. The node set is the CapacityBond active set, read directly from the contract (ADR 001).
  • Clients pick nodes from their peer store or the on-chain registry by measured RTT, stream content, and pay each node with signed vouchers drawn against one pool deposit (ADR 012, ADR 003).
  • On a cache miss, a node fills from its own origin backend if it has one. With node-to-node pull-through on (the default for a node with no origin backend), it can also pull from a peer and stream to the client at the same time. The node pays for that upstream pull from its own pool, like any other client: every byte transfer is paid.
  • Origin backends (S3, R2, B2, HTTP, filesystem) are private per-node config. No origin URL is ever exposed (ADR 002).

🧰 Commands

The node stack ships as two binaries, split like dockerd and docker (appendix): decdn-node is the daemon, with one subcommand, run; decdn is every command a human types. The sponsorship daemon and its onramp are two more (docs/sponsord/). Run decdn <command> --help for flags.

All decdn commands
decdn command Purpose
key-gen, whoami Generate, then inspect, the Ed25519 node key and Ethereum keystore
config {init,validate} Write a config file; check one before the daemon reads it
setup Guided operator onboarding over key-gen, node bond, and node register
node {…} Admin of a running daemon (health, status, lanes, pools, top, evict, reload, drain, slashes); the on-chain lifecycle (bond, unbond, register, deregister, rotate-key, update-multiaddrs, update-region); diagnostics (doctor, lookup)
probe Probe a node over cdn/probe/v1
get Paid pull over cdn/client/v1 of what a decdn: URI names: a blob, a directory bundle, or one file of a bundle (appendix)
quote Price a pull of a decdn: URI from the rates the nodes sign in their probe answers, before any paid byte
receipt {verify,settle} Check the pull receipt get --receipt wrote, offline; add the on-chain redemptions that settle it (appendix)
pool {…} Client payment-pool lifecycle: list, open, top-up, close, reclaim, assign
publish {…} Publisher control plane: namespace create; seat and unseat authorized origins with assign and revoke
origin {import} Write local content into a local filesystem origin store, offline and config-free
appeal {slash} File a slash appeal and post the appeal bond (ADR 028)
self-update Replace this binary with the latest release whose SHA256SUMS.minisig the embedded key verifies; --check only reports

The global --config <path> selects the config file, and every decdn-node run flag also reads a DECDN_* environment variable (decdn-node run --help). Every chain endpoint and contract address comes from the config file, and each command also accepts it as a flag (--rpc-url, --*-address). Operator terms accepted at registration live in crates/cli/TERMS.md, embedded verbatim in the CLI.

πŸ“ Architecture

For the full design, start at adr/README.md. It gives the reading order, and adr/architecture.md is the living overview.

Wire protocols
ALPN Purpose
cdn/probe/v1 Latency and availability probing
cdn/client/v1 All paid delivery, client→node and node→node
cdn/dht/v1 Content discovery between nodes via a Kademlia DHT (ADR 022)
Crates and contracts
crates/
  node/         β€” daemon binary `decdn-node`: runtime bring-up, handlers, admin RPC server, dispatch limiter
  cli/          β€” user CLI binary `decdn`
  common/       β€” config schema + resolver, identity loading, AdminRpc trait + DTOs, the `decdn` clap tree and the daemon's `run` flags
  protocol/     β€” wire format and ALPN message definitions (leaf crate)
  config-types/ β€” config-vocabulary value types shared by cache + common (leaf crate)
  bao-range/    β€” bao verified-range helpers and the origin-store layout (leaf crate, ADR 038)
  cache/        β€” cache engine wrapping iroh-blobs + origin pull-through
  client/       β€” `cdn/client/v1` paid-pull requester and buyer-side pool open, shared by node and cli
  incentive/    β€” payment pools, staking, vouchers (alloy for Ethereum)
  reputation/   β€” local per-peer reputation scoring (ADR 008)
  e2e/          β€” test-only Rust↔contract fixtures (anvil + the production deploy script)
  sponsord/     β€” sponsorship daemon binary `sponsord`: signs download capabilities against a PaymentPool, keeps it topped up
  sponsord-onramp/ β€” public onramp binary `sponsord-onramp`: the gate, the `decdn.sh` / `decdn.ps1` installers, the hand-off
  sponsord-core/ β€” the daemon's logic as a library, built on `client` and `incentive`
  sponsord-api/ β€” sponsord wire types, error codes and typed HTTP clients (leaf crate)
  sponsord-e2e/ β€” test-only: the daemon, the onramp and an installer run together
contracts/      β€” Solidity contracts + Foundry (excluded from the Cargo workspace)

The daemon is itself a paying client on its upstream cache-miss leg, so node depends on client like any other consumer; so does sponsord-core, which opens the pool a sponsor pays from. The leaf crates keep the publisher CLI, the client SDK and the sponsord servers free of any blob store or AWS SDK. .github/scripts/check_crate_edges.py enforces that boundary in CI.

The contracts cover operator bonding, USDC settlement and the fee split, buyback-and-burn, served-bytes governance, slashing and appeals, content takedown, and publisher namespaces. contracts/README.md lists each contract, and ADR 016 Β§ Contract Inventory maps how they interact.

Design at a glance
Area Design ADRs
Content Blobs are BLAKE3-addressed, and the receiver verifies every byte. The hash is a Merkle root, so any byte range verifies on its own, which makes resume and multi-source fetch safe. Publishers may encrypt content before upload; key distribution stays outside the protocol. 002, 038
Discovery Clients never query the DHT: they pick bonded nodes from their peer store or the CapacityBond registry by measured RTT. Nodes find holders for a cache miss through the cdn/dht/v1 Kademlia DHT; for a namespaced request, the on-chain origin set is the directory of last resort. Latency-driven proxy warming creates regional copies where demand is. 012, 022, 037
Delivery A large fetch spreads disjoint byte ranges across several holders, at most one per operator, and reassigns ranges as sources finish or stall. Cache admission and eviction are node-local. A node refuses a cache miss whose upstream price leaves no margin after the fee split. 039, 040, 041
Payments A client funds one shared PaymentPool and pays each provider with cumulative off-chain vouchers. Only the provider redeems its own vouchers, so a pool owner's close cannot understate what a node earned. A close starts a grace window in which nodes keep redeeming, and each node's periodic sweep redeems well inside it. 003
Tokenomics USDC pays for bytes. Operators bond TOKEN on a governance-tunable curve over declared bandwidth (super-linear at the default exponent), with a flat minBond floor. Fee-split buckets are governance-tunable. Protocol-owned liquidity is a Uniswap V3 50/50 TOKEN/USDC pool that governance seeds after launch. 026, 016, 018
Enforcement Every ProbeResponse and StreamResponse carries a secp256k1 EIP-712 slash_sig, so a node's quotes and deliveries are on-chain evidence; the Ed25519 NodeId authenticates connections and the registration proof, never slash evidence. A challenge is a commit–reveal that resolves on-chain at reveal time. Slashed TOKEN sits in escrow for a 30-day appeal window. Governance blacklists content hashes. Reputation is a local per-peer EWMA. 014, 028, 011, 008
Governance DecdnGovernor with a TimelockController. No TOKEN balance carries a vote. Vote weight is trailing-window served bytes, capped per epoch by declared capacity and overall by a per-operator share, ramped by tenure since first bond, and zeroed by a standing slash inside the trailing window. The process moves from admin key to bootstrap multisig to DAO; the contract set stays the same. 009, 036
Evolution Varint-length framing, protocol enums, and a three-tier schema-evolution model. Production targets Arbitrum One. 013, L2 appendix

πŸ“š Documentation

Topic Where
Project overview decdn.org
User and operator docs docs.decdn.org
Live testnet statistics stats.decdn.org
Protocol specification (ADRs) and glossary adr/, adr/glossary.md
Operating a node Operator runbook, key rotation, upgrade path
Monitoring Dashboards and alert rules (in decdn/devops); metric surface
Contracts contracts/README.md
Sponsored downloads docs/sponsord/: the daemon and onramp behind decdn.sh and decdn.ps1
Releases RELEASING.md: cutting, signing, and publishing a release

🀝 Contributing

CONTRIBUTING.md covers the environment, build, tests, code style, ADR conventions, the issue forms and the label scheme. Report security vulnerabilities privately as SECURITY.md describes.

License

Dual-licensed under either MIT or Apache-2.0, at your option.

About

Decentralized CDN where nodes cache and serve content-addressed blobs over iroh QUIC, and clients pay per-MB via off-chain USDC shared payment pools.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

46 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages