README.md
1 # Dappnode package development 2 3 1. [Description](#description) 4 2. [Server](#server) 5 3. [Install](#install) 6 4. [Connect](#connect) 7 5. [Considerations](#considerations) 8 6. [Development](#development) 9 7. [Publish](#publish) 10 8. [Limitations](#limitations) 11 9. [Known issues](#known-issues) 12 13 14 ## Description 15 16 Dappnode packages supports two types of [configuration](https://docs.dappnode.io/docs/dev/package-development/overview) 17 - [Single Configuration](https://docs.dappnode.io/docs/dev/package-development/single-configuration) - Used to generate a single package, tailored for a specific configuration. 18 - [Multi-Configuration](https://docs.dappnode.io/docs/dev/package-development/multi-configuration) - Used to generate multiple packages with varying configurations, such as different networks or client setups. 19 20 Provided guide is focused on Multi-Configuration variant because it provides more flexibility. 21 22 The easiest way to develop the package is to use a VM and in that guide we will use Hetzner Cloud. 23 24 - [Docs](https://docs.dappnode.io) 25 - [Package Development](https://docs.dappnode.io/docs/dev/package-development/overview) 26 27 28 ## Server 29 30 1. Run an Ubuntu VM on Hetzner - `8vCPU/16GB RAM` (`cx42/cpx41`) 31 2. Create firewall rules based on the [Cloud Providers / AWS](https://docs.dappnode.io/docs/user/dappnode-cloud/providers/aws/set-up-instance/) guide 32 33 | Protocol | Port | Service | Source | Comment | 34 | -------- | ------------ | ------------- | ----------- | ------------------------------------ | 35 | `TCP` | `22` | `SSH` | `0.0.0.0/0` | | 36 | `TCP` | `80` | `HTTP` | `0.0.0.0/0` | Required for services exposing only? | 37 | `TCP` | `443` | `HTTP` | `0.0.0.0/0` | Required for services exposing only? | 38 | `UDP` | `51820` | `Wireguard` | `0.0.0.0/0` | | 39 | `TCP` | `1024-65535` | `General TCP` | `0.0.0.0/0` | | 40 | `UDP` | `1024-65535` | `General UDP` | `0.0.0.0/0` | | 41 42 43 ## Install 44 45 1. We can install Dappnode on Ubuntu VM using [Script installation](https://docs.dappnode.io/docs/user/install/script/) 46 ```shell 47 # Prerequisites 48 sudo wget -O - https://prerequisites.dappnode.io | sudo bash 49 50 # Dappnode 51 sudo wget -O - https://installer.dappnode.io | sudo bash 52 53 # Restart 54 sudo reboot 55 ``` 56 57 58 ## Connect 59 60 > [!NOTE] 61 > Please wait for 1-3 minutes after node was started. 62 63 1. Check Dappnode status 64 ```shell 65 dappnode_status 66 ``` 67 68 2. Run Dappnode if not started 69 ```shell 70 dappnode_start 71 ``` 72 73 3. Get wireguard credentials and connect to the Dappnode instance - [WireGuard Access to Dappnode](https://docs.dappnode.io/docs/user/access-your-dappnode/vpn/wireguard/) 74 ```shell 75 dappnode_wireguard 76 ``` 77 78 4. Open http://my.dappnode in the browser. 79 80 81 ## Considerations 82 83 1. Users might run a lot of different packages, which can use some standard ports like `30303`, this is why we used different default ports 84 ```shell 85 30303 --> 40303 86 ``` 87 Just add 10000 to every port. 88 89 90 ## Development 91 92 1. Clone GitHub repository on local machine 93 ```shell 94 git clone https://github.com/codex-storage/DAppNodePackage-codex 95 96 # For new package run 'init' 97 # npx @dappnode/dappnodesdk init --use-variants --dir DAppNodePackage-codex 98 ``` 99 Add you changes to the code. 100 101 2. Copy package files to Dappnode server 102 ```shell 103 local_dir="DAppNodePackage-codex" 104 remote_dir="/opt/DAppNodePackage-codex" 105 host="root@<server-ip>" 106 107 rsync -avze ssh --rsync-path='sudo rsync --mkpath' "${local_dir}/" "${host}:${remote_dir}/" --delete 108 ``` 109 110 3. Install [Node.js](Node.js) on Dappnode server using [nvm](https://github.com/nvm-sh/nvm) 111 ```shell 112 # nvm 113 curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.0/install.sh | bash 114 115 # Load 116 export NVM_DIR="$HOME/.nvm" 117 [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm 118 119 # Node 22 120 nvm install 22 121 122 # Check 123 node --version 124 # v22.16.0 125 ``` 126 127 4. Install [DAppNodeSDK](https://github.com/dappnode/DAppNodeSDK) on remote Dappnode 128 ```shell 129 # Install 130 npx -y @dappnode/dappnodesdk 131 132 # Help 133 npx @dappnode/dappnodesdk --help 134 ``` 135 136 5. Get Dappnode IPFS IP: `Packages` --> `System packages` --> `Ipfs` --> `Network` --> `Container IP` 137 138 6. Build the package 139 ```shell 140 # Code directory - multi-arch builds failed with --dir argument 141 cd /opt/DAppNodePackage-codex 142 143 # Use Ipfs node IP 144 npx @dappnode/dappnodesdk build --all-variants --provider=http://172.33.0.2:5001 145 ``` 146 ``` 147 Dappnode Package (codex.public.dappnode.eth) built and uploaded 148 DNP name : codex.public.dappnode.eth 149 Release hash : /ipfs/QmR7HVCpyWyDLGswF5Z1FXebrr3XjbWZeYQV2bhq5e4v1Q 150 http://my.dappnode/installer/public/%2Fipfs%2FQmR7HVCpyWyDLGswF5Z1FXebrr3XjbWZeYQV2bhq5e4v1Q 151 ``` 152 153 7. Install the package via DAppStore and using IPFS CID from previous step and check `Bypass only signed safe restriction` 154 155 156 ## Publish 157 158 - [Package Publishing](https://docs.dappnode.io/docs/dev/package-publishing/publish-packages-clients) 159 160 161 ## Limitations 162 163 1. Dappnode packages are built on top of the [Docker Compose](https://docs.docker.com/compose/) which has limited configuration flexibility and DAppNodeSDK does not provide any workarounds. 164 2. Docker Compose base imply the following limitations 165 - Variables 166 - If we need to pass an optional environment variable, it needs to be defined in Compose file with some default value and it anyway will be passed to the container 167 - If that optional variable can't accept a blank value, we should undefine it conditionally in the Docker entrypoint 168 - Ports 169 - If we need to define an optional port forwarding, it needs to be defined in Compose file with some default values and it anyway will be active and take the port on the node 170 - There is no way to configure "really optional" port forwarding 171 - A workaround would be use a separate package variant, but it is to big overhead 172 3. We can't have a relation between variable and port forwarding, to setup same value using a single field. User have to fill separately two fields with the same value. 173 4. Multi-Configuration package does not provide a real flexibility, it just generate multiple separate packages and it doesn't work like a single package with multiple options during the installation. 174 5. [Using profiles with Compose](https://docs.docker.com/compose/how-tos/profiles/) is not supported. 175 6. There is no way to setup a custom service name during package installation and it is predefined in the main `dappnode_package.json` 176 - We can set an alias like `codex.public.dappnode --> codex-app.codex.public.dappnode` 177 - That can be done for a single service in the package 178 7. Is there a way to adjust container port for [Published ports](https://docs.docker.com/engine/network/#published-ports) or we can configure just host port? 179 8. File [`setup-wizard.yml`](<https://docs.dappnode.io/docs/dev/references/setup-wizard>) is not supported in [Multi-Config Package Development](<https://docs.dappnode.io/docs/dev/package-development/multi-configuration>) which is very confusing. And same issue is with the `getting-started.md`. 180 9. There is no way to setup custom service names for multiple services and they all namespaced under the `package name` 181 ```shell 182 # Public packages 183 geth.codex.public.dappnode 184 codex-app.codex.public.dappnode 185 codex-node.codex.public.dappnode 186 ``` 187 1. When we have Multi-Configuration package, we should define different package name for each variant, which imply different namespaces for services names and that looks not so nice, for example 188 ```shell 189 # Package codex 190 codex.public.dappnode --> codex-app.codex.public.dappnode 191 codex-app.codex.public.dappnode 192 codex-node.codex.public.dappnode 193 194 # Package codex-local-geth 195 codex-local-geth.public.dappnode --> codex-app.codex-local-geth.public.dappnode 196 codex-app.codex-local-geth.public.dappnode 197 codex-node.codex-local-geth.public.dappnode 198 geth.codex-local-geth.public.dappnode 199 ``` 200 If we would like to have separate packages, which would permit to use same handy URL like `codex.public.dappnode` for main service, and other services under that namespace, it would be required to have separate repositories(package folders) with the same package name. It can be a cosmetic point, but it highlights a limitation we have. 201 202 203 ## Known issues 204 205 1. During local package build it is uploaded to the local IPFS node, but in the Dappnode UI package avatar is loaded from the https://gateway.ipfs.dappnode.io, so most probably it will not be shown and it is not so clear what is the issue. Maybe something is wrong with avatar or something else? We can use default avatar, which is known by Dappnode IPFS gateway. 206 2. File `getting-started.md` is not specified in the official documentation, but it exists and is very usefully. 207 3. Dappnodesdk does not support `compose.yaml` file, [which is default and preferred](https://docs.docker.com/compose/intro/compose-application-model/). 208 4. Often time it can be more effective to [explorer existing packages](https://github.com/dappnode?q=DAppNodePackage&type=all&language=&sort=) configuration, than to use a documentation. 209 5. During the package build, Docker warn that ["the attribute `version` is obsolete"](https://docs.docker.com/reference/compose-file/version-and-name/#version-top-level-element-obsolete), but dappnodesdk will fail if we remove it - that is very confusing.