Goal
Let home-server users run a deCDN node on Umbrel with a few clicks, instead of the current path (Ansible site.yml → release-tarball binary under a hardened systemd unit). This needs a containerized node and an Umbrel app definition.
Scope assumption: this is about packaging the deCDN node (decdn-node), filed in decdn/devops as deployment/packaging tooling. The Docker image build may ultimately belong in decdn/decdn (where the binary is built) — see open questions. Re-scope if that's wrong.
Why there's work to do
There is no container image today — the node installs as a GitHub release tarball (decdn-node + decdn CLI, target x86_64-unknown-linux-gnu) and runs under systemd (see ansible/roles/decdn_node/). Umbrel runs apps as Docker containers via docker-compose, so we need: (1) a published multi-arch image, (2) an umbrel-app.yml + docker-compose.yml, and (3) a first-run config/onboarding flow.
Deliverables
- Dockerfile / image wrapping
decdn-node- Multi-arch: linux/amd64 (Umbrel Home) and linux/arm64 (Raspberry Pi). Note the release currently ships only
x86_64-unknown-linux-gnu — an aarch64 build artifact is a prerequisite. - Pin to a released
v<version>; verify the tarball sha256 (the role already supports decdn_node_sha256). - Non-root user, sensible entrypoint that runs
decdn key-gen on first boot if no keystore exists. - Published to a registry (GHCR
ghcr.io/decdn/decdn-node?).
- Umbrel app (
umbrel-app.yml + docker-compose.yml) for the community app store
- Persist
${APP_DATA_DIR}/data → /var/lib/decdn (holds node.secret, keystore.json, cache). - Expose public QUIC udp/4433 (
decdn_bind_port); keep metrics (9090) and admin (9191) loopback/internal. - Config surface (today these live in
node.toml + decdn.env): decdn_rpc_url (sensitive, may embed an API key), payment_channel / capacity_bond / slash_judge contract addresses, region, chain_id (default 421614 Arbitrum Sepolia), cache size, rate/MB.
- Onboarding docs — how a user supplies the RPC URL + contract addresses, funds/stakes the node, and forwards the QUIC port.
Runtime contract (from ansible/roles/decdn_node/defaults/main.yml)
| Item | Value |
|---|
| Binary | decdn-node (+ decdn CLI) |
| Release source | https://github.com/decdn/decdn/releases/download |
| Public port | udp/4433 (QUIC) |
| Loopback ports | metrics 9090, admin 9191 |
| Data dir | /var/lib/decdn (node.secret, keystore.json, cache) — 0700 |
| Sensitive config | DECDN_RPC_URL, eth keystore + password |
| Required knobs | rpc_url, 3 contract addresses, region, version |
Open questions
- Where does the Dockerfile live —
decdn/decdn (core, builds the binary) vs here (decdn/devops)? The Umbrel app definition itself goes to an Umbrel community-app-store repo. - arm64 release builds — does
decdn/decdn CI produce aarch64-unknown-linux-gnu artifacts? If not, that's an upstream prerequisite. - NAT traversal for home users — QUIC udp/4433 needs a port-forward; document/encourage the optional iroh relay (
decdn_relay_url) for users who can't forward. - Keystore / staking UX — on-chain stake + registration (ADR 019 Phase 2) is an operator step; what's the minimal Umbrel-friendly flow (generate key in-container, show address, user funds + stakes externally)?
- No baked protocol facts — chain-id, addresses, rate must come from operator config / ADRs, never hardcoded in the image (per repo
CLAUDE.md).
Out of scope
The anvil devnet (internal tooling) — this is the public node only.
Goal
Let home-server users run a deCDN node on Umbrel with a few clicks, instead of the current path (Ansible
site.yml→ release-tarball binary under a hardened systemd unit). This needs a containerized node and an Umbrel app definition.Why there's work to do
There is no container image today — the node installs as a GitHub release tarball (
decdn-node+decdnCLI, targetx86_64-unknown-linux-gnu) and runs under systemd (seeansible/roles/decdn_node/). Umbrel runs apps as Docker containers viadocker-compose, so we need: (1) a published multi-arch image, (2) anumbrel-app.yml+docker-compose.yml, and (3) a first-run config/onboarding flow.Deliverables
decdn-nodex86_64-unknown-linux-gnu— anaarch64build artifact is a prerequisite.v<version>; verify the tarballsha256(the role already supportsdecdn_node_sha256).decdn key-genon first boot if no keystore exists.ghcr.io/decdn/decdn-node?).umbrel-app.yml+docker-compose.yml) for the community app store${APP_DATA_DIR}/data→/var/lib/decdn(holdsnode.secret,keystore.json, cache).decdn_bind_port); keep metrics (9090) and admin (9191) loopback/internal.node.toml+decdn.env):decdn_rpc_url(sensitive, may embed an API key),payment_channel/capacity_bond/slash_judgecontract addresses,region,chain_id(default 421614 Arbitrum Sepolia), cache size, rate/MB.Runtime contract (from
ansible/roles/decdn_node/defaults/main.yml)decdn-node(+decdnCLI)https://github.com/decdn/decdn/releases/download/var/lib/decdn(node.secret,keystore.json, cache) — 0700DECDN_RPC_URL, eth keystore + passwordOpen questions
decdn/decdn(core, builds the binary) vs here (decdn/devops)? The Umbrel app definition itself goes to an Umbrel community-app-store repo.decdn/decdnCI produceaarch64-unknown-linux-gnuartifacts? If not, that's an upstream prerequisite.decdn_relay_url) for users who can't forward.CLAUDE.md).Out of scope
The anvil devnet (internal tooling) — this is the public node only.