From 408ad747b584d839da5a0740bb7b0e6ade9ad7ae Mon Sep 17 00:00:00 2001 From: Alper Gundogdu Date: Thu, 24 Sep 2026 10:29:45 +0100 Subject: [PATCH 1/3] docs: route role onboarding through decdn_chain; whoami and IPv4-only notes The decdn_node role README still showed bare `decdn setup` / `node bond` / `node register`, which fail on a role-deployed node with "rpc_url not set": on-chain CLI commands resolve rpc_url only from --rpc-url or blockchain.rpc_url, and the role keeps the URL in /etc/decdn/decdn.env. Use the decdn_chain helper from docs/lifecycle.md there, and state the argv-exposure tradeoff it carries. - lifecycle.md: name the upstream fix (env = "DECDN_RPC_URL" on CommonChainArgs.rpc_url), add a whoami command for the wallet address (keystore.json has no plaintext address), and say to register /ip4/ only. - requirements.md: the QUIC listener binds 0.0.0.0:4433, so "IPv6: supported" was misleading for the node itself. Co-Authored-By: Claude Opus 5.5 --- ansible/roles/decdn_node/README.md | 33 +++++++++++++++++++++--------- docs/lifecycle.md | 18 +++++++++++++++- docs/requirements.md | 8 +++++--- 3 files changed, 45 insertions(+), 14 deletions(-) diff --git a/ansible/roles/decdn_node/README.md b/ansible/roles/decdn_node/README.md index a608a2c..4608c76 100644 --- a/ansible/roles/decdn_node/README.md +++ b/ansible/roles/decdn_node/README.md @@ -428,27 +428,37 @@ See `roles/decdn_node/defaults/main.yml` for the full knob list, defaults and un The role stops at ADR 019 Phase 1 (host prep) and Phase 3 (startup). Phase 2 — fund, bond, register — is the operator's, but it is **no longer a set of raw contract -calls**: upstream ships a guided CLI. Every one of these takes `--dry-run`. +calls**: upstream ships a guided CLI. Every one of these takes `--dry-run`. They need +`--rpc-url` (see "The RPC URL is the exception" below), so on the node run them +through the `decdn_chain` helper from +[`docs/lifecycle.md` § Running on-chain commands](../../../docs/lifecycle.md#running-on-chain-commands), +which runs as `decdn` with `/etc/decdn/decdn.env` and adds `--config`, `--rpc-url` and +`--keystore-password-file`. ```bash # Guided path: pre-flight checks (clock skew, gas, balances), key generation, # bond and registration, ending in a readiness summary. Thin orchestration over # `key-gen` / `node bond` / `node register` — it submits no transaction they do # not. (The exit path below is NOT part of setup.) -decdn setup --mbps 100 --region US \ +decdn_chain setup --mbps 100 --region US \ --multiaddr /ip4//udp/4433/quic-v1 --yes --accept-terms # Or drive the primitives directly: -decdn node bond --mbps 100 # CapacityBond.bond + declareMbps (idempotent: - # tops up only the shortfall, so a re-run after a - # partial failure converges rather than over-bonding) -decdn node register --region US \ +decdn_chain node bond --mbps 100 # CapacityBond.bond + declareMbps (idempotent: + # tops up only the shortfall, so a re-run after a + # partial failure converges rather than over-bonding) +decdn_chain node register --region US \ --multiaddr /ip4//udp/4433/quic-v1 \ - --accept-terms # CapacityBond.registerNode — builds the EIP-712 - # binding + ed25519 ownership signatures locally. - # --region is REQUIRED (no default). + --accept-terms # CapacityBond.registerNode — builds the EIP-712 + # binding + ed25519 ownership signatures locally. + # --region is REQUIRED (no default). ``` +The multiaddr must be `/ip4/`. The daemon binds QUIC on `0.0.0.0:4433`, IPv4 only. +To get the address to fund, run `decdn --config /etc/decdn/node.toml whoami +--keystore-password-file /etc/decdn/keystore.password` as `decdn` (it's in the same +section of `docs/lifecycle.md`). `keystore.json` has no plaintext address field. + Exiting is the reverse, in order: `decdn node deregister` (leaves the active set and clears the declared tier — the bond stays deposited and **fully slashable**), then `decdn node unbond --all` (starts the unbonding window; the node is INACTIVE @@ -469,7 +479,10 @@ renders (`--config /etc/decdn/node.toml`), so those only have to be right once. environment (only the daemon does), and this role keeps `rpc_url` out of `node.toml` because it may embed an API key. Pass it with `--rpc-url`. The `decdn_chain` helper in [`docs/lifecycle.md` § Running on-chain commands](../../../docs/lifecycle.md#running-on-chain-commands) -does that from `/etc/decdn/decdn.env`, as the `decdn` user. +does that from `/etc/decdn/decdn.env`, as the `decdn` user. The tradeoff: the URL sits +in the `decdn` process's argv while the command runs, where other local users can see +it with `ps`. The section there covers the shared-host workaround and the upstream fix +(`env = "DECDN_RPC_URL"` on the CLI's `--rpc-url`). ## Network diff --git a/docs/lifecycle.md b/docs/lifecycle.md index fccdfc4..ebda033 100644 --- a/docs/lifecycle.md +++ b/docs/lifecycle.md @@ -187,7 +187,23 @@ decdn_chain setup --mbps 100 --region DE \ The URL is in the `decdn` process's arguments while the command runs, so other local users could read it with `ps`. On a shared host, write a `0600` copy of `node.toml` -with `rpc_url` set under `[blockchain]` and pass that as `--config` instead. +with `rpc_url` set under `[blockchain]` and pass that as `--config` instead. The clean +fix is upstream: give `CommonChainArgs.rpc_url` (`crates/common/src/cli/common.rs`) +`env = "DECDN_RPC_URL"`, as the daemon has. Then `EnvironmentFile=` alone would be +enough and the helper could drop `--rpc-url`. + +Register an `/ip4/` multiaddr only. The daemon binds QUIC on `0.0.0.0:4433` (IPv4 +only), so an `/ip6/` address on-chain points at a port nothing listens on. + +To fund the wallet you need its address, and `keystore.json` has no plaintext address +field. `whoami` decrypts it. It takes no `--rpc-url`, so run it directly rather than +through `decdn_chain`: + +```bash +sudo systemd-run --pty --wait --collect -p User=decdn \ + /usr/local/bin/decdn --config /etc/decdn/node.toml whoami \ + --keystore-password-file /etc/decdn/keystore.password +``` ## Compose and Kubernetes diff --git a/docs/requirements.md b/docs/requirements.md index fe6105f..e6569ee 100644 --- a/docs/requirements.md +++ b/docs/requirements.md @@ -48,9 +48,11 @@ On Ubuntu 25.10 and later, `sudo` is sudo-rs; see the `ansible_become_exe` note at a latency cost. A node that serves paid traffic should be directly reachable, and the multiaddr it registers on-chain must be its public address (`/ip4//udp/4433/quic-v1`). -- **IPv6:** supported. On Ansible, `baseline_preserve_ipv6_autoconf` (default `true`) - keeps SLAAC addresses alive under DevSec hardening; set it `false` on static-IPv6 - hosts. Multi-homed hosts may need `baseline_rp_filter_loose: true`. +- **IPv6:** the host can keep it, but the node's QUIC listener is IPv4-only (it binds + `0.0.0.0:4433`), so the node needs a public IPv4 address and registers an `/ip4/` + multiaddr. On Ansible, `baseline_preserve_ipv6_autoconf` (default `true`) keeps + SLAAC addresses alive under DevSec hardening; set it `false` on static-IPv6 hosts. + Multi-homed hosts may need `baseline_rp_filter_loose: true`. - **Loopback only, never exposed:** metrics `127.0.0.1:9090`, admin RPC `127.0.0.1:9191`. Reach them over SSH, or ship metrics with the monitoring options. - **Clock:** keep NTP running (`baseline` installs chrony). `decdn setup`'s pre-flight From 7ec3fde12d4741ed58253cc17630fcab0cae7d7a Mon Sep 17 00:00:00 2001 From: Alper Gundogdu Date: Thu, 24 Sep 2026 11:15:52 +0100 Subject: [PATCH 2/3] docs(ansible): put the whoami command in a fenced block Co-Authored-By: Claude Opus 5.5 --- ansible/roles/decdn_node/README.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/ansible/roles/decdn_node/README.md b/ansible/roles/decdn_node/README.md index 4608c76..e6f1608 100644 --- a/ansible/roles/decdn_node/README.md +++ b/ansible/roles/decdn_node/README.md @@ -455,9 +455,14 @@ decdn_chain node register --region US \ ``` The multiaddr must be `/ip4/`. The daemon binds QUIC on `0.0.0.0:4433`, IPv4 only. -To get the address to fund, run `decdn --config /etc/decdn/node.toml whoami ---keystore-password-file /etc/decdn/keystore.password` as `decdn` (it's in the same -section of `docs/lifecycle.md`). `keystore.json` has no plaintext address field. +To get the address to fund, run `whoami` as `decdn` (also in that section of +`docs/lifecycle.md`). `keystore.json` has no plaintext address field. + +```bash +sudo systemd-run --pty --wait --collect -p User=decdn \ + /usr/local/bin/decdn --config /etc/decdn/node.toml whoami \ + --keystore-password-file /etc/decdn/keystore.password +``` Exiting is the reverse, in order: `decdn node deregister` (leaves the active set and clears the declared tier — the bond stays deposited and **fully slashable**), From 4424bba1df89387c5bb334cbb2b778ff5d3eb55e Mon Sep 17 00:00:00 2001 From: Alper Gundogdu Date: Thu, 24 Sep 2026 11:54:31 +0100 Subject: [PATCH 3/3] docs: register /ip6/ multiaddrs too on builds with decdn/decdn#2144 decdn/decdn#2144 (869141e9) binds QUIC on [::]:bind_port as well as 0.0.0.0:bind_port, falling back to IPv4-only (with a warn) on hosts without IPv6. The IPv4-only wording this PR added is now stale for new builds: - docs/lifecycle.md: register the /ip6/ multiaddr too on dual-stack hosts, check `ss -ulpn` shows [::]:4433 first, keep /ip4/-only for older builds, and add it after an upgrade with update-multiaddrs passing both addresses (it replaces the on-chain set). - docs/requirements.md: IPv6 is supported alongside IPv4 since #2144. - roles/decdn_node/README.md: same, next to the register example. Co-Authored-By: Claude Opus 5.5 --- ansible/roles/decdn_node/README.md | 5 ++++- docs/lifecycle.md | 10 ++++++++-- docs/requirements.md | 9 +++++---- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/ansible/roles/decdn_node/README.md b/ansible/roles/decdn_node/README.md index e6f1608..1705c4a 100644 --- a/ansible/roles/decdn_node/README.md +++ b/ansible/roles/decdn_node/README.md @@ -454,7 +454,10 @@ decdn_chain node register --region US \ # --region is REQUIRED (no default). ``` -The multiaddr must be `/ip4/`. The daemon binds QUIC on `0.0.0.0:4433`, IPv4 only. +On a dual-stack host, add `--multiaddr /ip6//udp/4433/quic-v1` as well. +Builds since decdn/decdn#2144 (`869141e9`) bind QUIC on both `0.0.0.0:4433` and +`[::]:4433`; check `ss -ulpn` shows `[::]:4433` first. Older builds are IPv4-only on +that port, so register `/ip4/` only there. To get the address to fund, run `whoami` as `decdn` (also in that section of `docs/lifecycle.md`). `keystore.json` has no plaintext address field. diff --git a/docs/lifecycle.md b/docs/lifecycle.md index ebda033..eadf088 100644 --- a/docs/lifecycle.md +++ b/docs/lifecycle.md @@ -192,8 +192,14 @@ fix is upstream: give `CommonChainArgs.rpc_url` (`crates/common/src/cli/common.r `env = "DECDN_RPC_URL"`, as the daemon has. Then `EnvironmentFile=` alone would be enough and the helper could drop `--rpc-url`. -Register an `/ip4/` multiaddr only. The daemon binds QUIC on `0.0.0.0:4433` (IPv4 -only), so an `/ip6/` address on-chain points at a port nothing listens on. +Register the node's public `/ip4/` multiaddr, and on a dual-stack host its `/ip6/` one +too (repeat `--multiaddr`). Since decdn/decdn#2144 (`869141e9`) the daemon binds QUIC +on both `0.0.0.0:4433` and `[::]:4433`; on a host without IPv6 it starts IPv4-only and +logs a `warn`. Check `ss -ulpn` shows `[::]:4433` before registering an `/ip6/` address. +An older build binds IPv6 on a random port, so an `/ip6/` address on-chain would point +at a port nothing listens on: register `/ip4/` only there. To add the `/ip6/` address +after an upgrade, run `decdn_chain node update-multiaddrs` with **both** addresses; it +replaces the whole on-chain set. To fund the wallet you need its address, and `keystore.json` has no plaintext address field. `whoami` decrypts it. It takes no `--rpc-url`, so run it directly rather than diff --git a/docs/requirements.md b/docs/requirements.md index e6569ee..034c1e5 100644 --- a/docs/requirements.md +++ b/docs/requirements.md @@ -48,10 +48,11 @@ On Ubuntu 25.10 and later, `sudo` is sudo-rs; see the `ansible_become_exe` note at a latency cost. A node that serves paid traffic should be directly reachable, and the multiaddr it registers on-chain must be its public address (`/ip4//udp/4433/quic-v1`). -- **IPv6:** the host can keep it, but the node's QUIC listener is IPv4-only (it binds - `0.0.0.0:4433`), so the node needs a public IPv4 address and registers an `/ip4/` - multiaddr. On Ansible, `baseline_preserve_ipv6_autoconf` (default `true`) keeps - SLAAC addresses alive under DevSec hardening; set it `false` on static-IPv6 hosts. +- **IPv6:** supported alongside IPv4. Since decdn/decdn#2144 (`869141e9`) the node's + QUIC listener binds both `0.0.0.0:4433` and `[::]:4433`, and a dual-stack node + registers both its `/ip4/` and `/ip6/` multiaddrs; older builds are IPv4-only on + 4433. On Ansible, `baseline_preserve_ipv6_autoconf` (default `true`) keeps SLAAC + addresses alive under DevSec hardening; set it `false` on static-IPv6 hosts. Multi-homed hosts may need `baseline_rp_filter_loose: true`. - **Loopback only, never exposed:** metrics `127.0.0.1:9090`, admin RPC `127.0.0.1:9191`. Reach them over SSH, or ship metrics with the monitoring options.