/ docs / README.md
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.