# If It's Not a Dappnode It Doesn't Exist: How To Create a Dappnodes Package for Your Node/Infra

- Channel: [Dappcon](https://streameth.org/dappcon)
- Date: 2025-06-18
- Duration: 53:15
- Watch: https://streameth.org/watch/6852defd90bd41297b9c3772

## Transcript

Hey, good morning, guys. So, today, I'm going to do a little workshop how to build a relatively simple DevNode package. I hope that you know what DevNode is, so that's why you're here. But I'm wondering whether you have done anything on DevNode, any of you. All right. And that's not that important. The important thing is for you to be able to follow workshop, you need to be familiar with Docker, a little bit. Okay. So, I'll show the flow. So, my idea is to make a package like line in front of you and that is not like crypto package because DevNode is more than just like a staking machine. It can do much more. It's your own server, so you can do whatever. And I had the idea of doing the OSIS package. OSIS means on cloud infant scale. That's the newest product, newest release of on cloud open source service, so I can show you. So, my idea is to build this as a DevNode package. So, that's your own cloud storage that you host, you don't share your data with anybody, and it's completely private. So, DevNode can do that and can do that easily, and I'm going to demonstrate you. So, I'm going to show you like real process, how I do it, and it's a bit of iterative process because at the front, I don't know what's going on, what are the package requirements, and it's a bit of a trial and error situation. So, first, for us to be able to do a package easily, we would like that the package has its like official Docker image, and that simplifies process massively. So, you see, the easiest way to find that is to go to the docs of a package and go to, for example, deployment, and try to find some kind of basic setup or getting started or something that shows you how to run this first with Docker, and then like within DevNode. Okay, I found this in docs, which redirects us to this, which redirects us back to the same page. Okay, I think it's in basic remote setup. So, Docker compose, development examples, basics. I found this a day ago. So, we need Docker. Let's do it like this. So, discover OSIS with Docker. Yeah, this is it. So, these are the OSIS docs. We are still not doing any DevNode related stuff that tell us how to deploy OSIS with Docker. And if we scroll down a bit, it tells us to prepare directories. That's fine. To do the ownership of directories, that's fine. We don't care about that. We care about this Docker pull. Okay, we need to pull the image. And we need to run this Docker command, which initializes the OSIS. And then we have to run this bigger command that runs OSIS indefinitely. So, that's like a bit of anti-pattern in DevNode, because in DevNode, we just want it to start, run it, and that's it. But we can solve that. So, now that we have the idea of how it works, we can go to our own machine, and we can choose a folder where we are going to build stuff. So, on my PC, I do it in code DevNode folder. It doesn't matter. And let's create the folder where we are going to build our project in. So, as a standard, we name them like this, DevNode package, dash, whatever name of the project is. Let's call it OSIS. So, we change that folder, and now we start coding. So, we have a tool called DevNode SDK that's meant to facilitate this process. And first thing that we need to do in this folder is initialize DevNode repo. So, we use DevNode SDK command, but we won't publish anything still. And we will do init command. And this will guide us now through the defaults. We will use the package that's default from the folder name. We will use version. We will type a description later. I'll tell you where these fields are. For order, we'll go to DevNode. License GPL 3.0, doesn't matter right now. And that's it. We are now initialized. And we can open our IDE. Thank you for that. And now we have basic structure of our DevNode repo. We start for Docker file because that's the first thing we see. And we, what we do with the DevNode package is we just take the, whatever we are doing, whatever package we are doing, in this case OSIS, we take that image as a base image to our own image that we are building with the DevNode. So, you see here we have, we know that name of the image is onCloudOSIS. And what we do is we say from onCloudOSIS, that means that our entry point is onCloudOSIS official image. But we won't have version tracking of this because when you do this, this will pull the latest image and then you won't know what you are doing. And then what we generally do is we tag them. And we always use upstream version variable, but we need to define it first. And we will define that in Docker compose. I'll tell you later why. But here we just need to like declare it. And we declare it as argument upstream version from OSIS Cloud. And that's it. For now, what we need to add here is that upstream version variable. Here, 8.1.vlsys version, it will be upstream version. And we want it to be the latest one there is. So, we'll just Google it. So, we'll just Google it and we'll see what's the latest version. Because we want to build on top of the latest version, there is no sense in building anything else. And that's 7.1.3. And we define it at 7.1.3. And this is our like basic, basic setup. So, what we can do now is we can try building this. Obviously, it won't work because we saw here that we first need to run this init, init command. But it does better. Like we can just try building it. And now, my connection to the dotnet is broken because I haven't connected to the VPN. So, what build command does is uses your Docker to build the package and then to upload it to IPFS. If you don't have IPFS endpoint, you can't do it. But luckily, so, I have Docker here in my wire guard and I can connect to it. So, if you are following me, if you are trying to do, no, no, it's fine. So, if you are following me and you want to do this with me, there is a way to do this without having to connect your dotnet. So, what you then have to do is you have to add additional argument variables, which is called provider, which is IPFS provider. And then you write either like HTTP of IPFS API or you use like our own that we use for developing, but we often purge it. So, you cannot count on it. So, we call it remote. And if you do this, this will work even without connecting to your dotnet. But as I'm connected to my dotnet and I don't need a remote provider, I can just use dotnet SDK build. And now it's a building an image. And it takes a while. And it's obviously already built it because our Docker file is very simple. So, it's just now saving and compressing and later it will upload it to IPFS. So, this obviously won't work because we didn't, but I'll show you the process at least now and then later we will iterate more quickly. So, while this is happening, does anybody have any questions? Is anything clear? Everything clear? All right. So, we can wait a while. So, this is being built. And while it's going on, we can see here in more detail what we need to do. So, first we need to init this thing. And then we will get some kind of user and password. I don't know right now what that is, but we will figure it out. All right. So, it has been built very quickly. And it has been uploaded to my IPFS. So, from here I can try to install it on my dev node. So, I've got the link here and I can follow it. So, this is open on my dev node. And now it will try to load it. At some point. So, let's do this. This will make it load more quickly, I hope. Yes. So, I have the basic package now built. It doesn't look very pretty. It doesn't have an avatar. It says that release is not signed, that it's unsafe. And if you try to update it, you won't be able to because you have to check the advanced options to bypass restrictions that we have. These restrictions are because when users are mostly using that node in order to stake and to validate and they have validators and everything. And since the SDK is permissionless and anybody can create DevNode packages, anybody could just create a package that's called Prism Consensus Validator, for example, and people would just download it thinking that it is. But if it's not signed by the original poster of the Prism package, then it will not be accepted. So, packages include a signature with the developer key and the DevNode can recognize when somebody is trying to impersonate another package that's not been signed by the developer address that's allowed to sign this. Yes, exactly. So, DevNode's philosophy is you can do whatever you want. You can install whatever package you want, but we want you to know what you are doing. So, it's not signed. That means it has been released by whomever, but you can install it and you do it on your own risk. But we have a very robust system that tells you from us that the package is signed and then you can trust the author, either us or the developer of that package, whoever that is. So, I'm going to try and install it now. I will click on update because I already have like a backspace called osys and it will try to install it. And the first that we do when we install a package is we look at the logs and see what's wrong. Because on first try, it's almost going to be something wrong with it. I mean, we are on purpose doing something wrong right now. But, yeah, I want to show the process because I've never built a package in the first try. So, yeah, I want to show you how it is. So, if you go to the logs, so, yeah, it's like giving me some weird error and nothing works. Because when we go back to the docs, we have to do first time initialization. And here we see that we need to provide it with two volumes. So, we will do that right away first. Volumes are both same in this docker run and this docker run so we can safely mount them. So, what we can do is we can write volume command and my copilot is telling me what to do. I think almost correctly because I don't want these to be bind mounts. I want these to be real docker volumes. So, what we do, this is basic docker stuff. We just specify these volumes. And declare volumes here as per docker spec. What the SDK for some reason wants is that you add these empty brackets so your validation passes. All right. So, this is the this part. The next part what we need to do is this own cloud in it step. And since we need to do these both steps inside of the docker volume, the package and without interaction with the user. So, what we do is usually we write a simple bash script that help us deal with that. And usually we call it entry point.sh. So, I will create that. I will make it executable. And now we can we can write our entry point script. So, but because we wrote our entry point script, we would need to change our docker file. So, it's not as simple as it was a few moments ago. And then we will change it. First, we will set work directory to the standard one in the user source app. This is irrelevant from the perspective. We would need to change. We would need to copy our entry point to the destination. And we have to specify now with the docker command entry point that our entry point is the entry point. And this is our docker file that we are going to use. Ah, thanks. So, that's pretty much it for our docker file. And all the complex logic happens here. So, let's save our docker file. We save our docker compose. And in the entry point, we try to do what's been done here. So, these are two steps. So, one is that we need to init. And second step is that we need to run it like this. So, first step is, as I said, process init. What's the name? And it says, we need to do it with insecurious. So, what this means for, like, I don't know for right now, but what we can do is what we can SSH, for example, in some Linux machine. And then we can try docker run. For example, help. You can't find the image, but it will download it. And then this is help command. So, maybe we can do all this init help. And this tells us usage of the init command. So, it says options that we have. And the default values. So, allow insecure OSIS context. That doesn't sound benign. But it's okay. Because everything is tucked in docker. So, we don't need to worry about it. But it says admin password and set admin password and use generated one. So, it says here that when you are doing OSIS init command that your password is randomly generated if you don't send admin password value. And that's something that we do not like, because that would need users wouldn't be able to set their password. And their password would be when they would like to fish it out of the locks. So, that's not very optimal. So, what we can do is using this OSIS init command, we can set admin password. To some value. And let's call that value OSIS admin password. So, that's some kind of variable that user will be able to set. So, in that node, we interact with the packages using environment variables mostly. So, what we do is we need to set this environment variable and we do that in Docker compose. So, Docker compose is a file that reads and builds your interface around the package around it. So, whatever environment variable is defined in the compose, it is shown on the URL. So, environment admin password equals, let's do it like this. We'll set it up. So, that user is forced to set, yes. Oh, okay. So, I will zoom in. So, the question was, can I zoom in? Yeah, definitely. Is it better? Yeah, I can do it even more if you want. So, yeah. Right now, we have our entry point set. Environment set that we need to insert admin password and we are doing our OSIS init. Nice. Next, after we've done OSIS init, we need to do this command. And this command is a bit tricky because it is using default entry point and we don't know what that is. But we can easily find out in, for example, for the OSIS, it's easier to do it in the GitHub. Oh, excuse me, Docker Hub. And you can see here that entry point, default entry point is OSIS. I'll zoom it. And the command is server. That means what we can do is we can do just OSIS server. And that should be good enough, except the rest of the environment variables. So, for running OSIS, we need to expose the port. It says 9200. We'll set the default. We don't have reason to change it. So, we can expose the port. Actually, we don't, let me backtrack on that. We don't, in that mode, so how that mode works is that you are connected to your own network. So, when you connect to that mode, you connect to the internal Docker network of the depth node. So, you don't have to expose the ports. But to be able to access every possible port of the container. So, we'll skip this one. But we would need to copy these environment variables. So, we'll need to figure out what these are. Not let them copy for now. So, OSIS insecure equals true. We've already said that. Proxy, HTTP proxy address. This, to keep this more dynamic, the HTTPS proxy address is the address on which the internal process is going to listen in OSIS. So, because the default port is 9200, we'll set it to 9200. And 000 means that OSIS will listen to all interfaces that are available to them. And that is a requirement where we are doing anything Docker related. And this last one, OSIS URL, is an interesting one. Because it makes a set URL that we will be accessing OSIS from in advance. And so, when you install a Docker package, it automatically gives you domain names for your package. And I can tell you if you do public package, which this is. So, if you go over here, it says OSIS public package. This is actually domain that you are able to connect to your package, whatever it is. So what we can do here is we can just copy-paste it there and we should be set. All right. So we can double-check. We change the environment. We did the volumes. And that's pretty much it. We did init. And yeah, what we can do next is we can try to build the package again and see if it works. Build. Again, it has been built very quickly because this Dockerfile is extremely simple. But it doesn't mean that everything is going to go smooth. So the next step of the process is we iterate, as I said. So we build the package. We think it could be good. We install it and see what's going on. So as before, while we wait for this, is there any questions? All right. It should be done very quickly as it is. Pretty lightweight package, I think. We have it here. And you see in the package UI, it picked up the version of our package and the upstream version because DevNodes understands upstream version argument in the compose. So we have it here. We can have it installed. It's, again, not fine. We can bypass save disclaimer. But what I'm going to show you is usually when I'm building a package, I usually click this show advanced editor. And that advanced editor shows us what we have set in the compose over here. We have four environment variables. We have these set. And these volumes. And we do not want custom mount path. We want the default. And what we need to do is set the password. We can set it as DevCon. And now it's in the plain text, but we can change it later. So accept, save, and it's been installed. So because I'm using my own DevNode when I'm building a package, and package gets uploaded to IPFS node of my DevNode, it gets downloaded very quickly because it's on the same machine. Yes, so we have it installed. Let's see logs. All right. It says that it generated OSIS config. That seems fine. Let's try to go to the OSIS URL we specified here and see if it's going to work. Okay. So this connection is not private. That happens because we are using HTTPS here. And then we have to go around this. So this is limitation I found. Yeah, and then we have our own package. We can go admin, DevCon, and we're in. So we build like version zero of our package. So this is only right now this is only accessible from DevNode. It has this DevNode private domain, and you can use it. It has this ugly warning that your connection is not secure. And as I said, that's OSIS thing because it forces us to have HTTPS connection wherever. So in DevNode, so when you access your DevNode, we build it that you are either on VPN, you are on the private Wi-Fi, or you are using the tail scale, which is also kind of VPN. So necessarily it's not required to have HTTPS connection for you to be safe. Because you are in a sandbox secure environment. And usually when we build packages, we do not use HTTPS. For example, I have Rotki here. And when I click on the UI here, you see Rotki here is not using HTTPS. It's not shown, but it's not using HTTPS. But if we try to do the same thing with OSIS, for example, if we change URL, we want it to be HTTP and update it. This will now change the environment and restart the package. Yes? Can you have a microphone, please? Sorry, one thing I didn't follow. I missed early on. This .DevNode address, so you're on your laptop there, and you're talking to your DevNode? Yes. How is that resolving? Yeah, because I'm using VPN here, which is connected to my DevNode. And when you are using VPN, you can set up on your DNS server. And that DNS server is also a DevNode package. You can see it here. And it's made to resolve, in this case, Docker aliases. And every packaging style has Docker aliases that ends with a DevNode. Your main UI, we call it DevManager, has my.DevNode domain. And that's how those are resolved. Okay, got it. Any other questions? All right. So, now we can see that our package is crashing. It can't start. And it locks. So, yeah. Could not create config. Config file already exists. All right. So, that's a bug with our package. So, what we did is we just, in our entry point, we just did OSIS init and OSIS server. So, we have two solutions to this. We can use force overwrite flag. Or we can do some if-else in here if we want this to execute only once. For example, we can check for a file. And if that file exists, we don't do it. And if it doesn't exist, we do OSIS init and initialize the file. But in this case, I think it's better for us to use this force overwrite flag. Because in other case, the init file, init will be run only once. And in case we change our admin password, this won't be run again. So, I think it's better to do force overwrite here. And then when we change in our config admin file, this init will be changed again. So, yeah. Let's do it like that. And we can rebuild our package. But because this config file is baked into our volumes, what we can do is we can now remove the volumes. And that will fix our issue because we deleted our config file. But that's just temporary so we can see what's going on. Now we have a different error. So, this is one I was aiming for before. So, it says, you know, the ISSI URL must start with HTTPS. So, OSIS is forcing us to do HTTPS. And that's why I did it here first time. So, we can update this, delete volumes. And now we should be up and running again. So, let's see logs. That's called admin. I don't see any logs, but. Yeah, the same warning again. Visit the website. Okay. We are good. So, what we can do is we can install the fixed version of our package now that has overwritable flag set. But I want to show a couple of more things. So, we won't be installing this one. We will install the next iteration. So, as you saw, when we did Docker init, it gave us a couple of those default files. And one of those is this Docker package file, which tells the depth node whatever we have in info here. But we have a lot more possibilities here. What we can do is we can specify UI here. So, UI here is HTTPS. What was it? What was it? This one. So, we want users not to guess the URL where stuff happening. And just to have a nice link here. Also, we can do description of a package. This shows up when we are installing. But to see full possibilities here, we can go to the depth node docs and we have depth section. And the most useful to me is this file references section because it says Docker compose. We have that manifest. We have that as well. So, there are a lot of possibilities here. These are pretty straightforward what you can do. Architecture that you want to be built, the requirements, dependencies, types, descriptions, options, version, whatever. But I want to show you the setup wizard. When we were installing the package, we typed in the environment variable that's called admin password. It's sketchy. It was in plain text and that's something that you don't do. That's why we have this setup wizard. That's YAML as well. That builds up our setup wizard so the package installation process is a bit more presentable. So, we have an example here that I'll copy. And I will create a setup wizard here. And I'll paste it and we'll change it to our needs. So, what we want to do with the setup wizard is have a nice field to set admin password in. So, ID is not relevant that much. We will call it admin password, for example. So, what's our target? Our target is environment and name of the environment variable is admin password. So, we go to setup wizard. We change that to the admin password and service. Service is this. So, that refers to the Docker service. Docker Compose service that we are aiming to inject environment variable to. So, we change that to service title. OSIS UI admin password. So, description of that variable is, I don't know, whatever the co-pilot said. It's secret is true. That means that we'll be masked on the UI. This is a pattern that we wanted to match. We don't want it to match any pattern. It's not animal. It's just a string. So, we just need these fields to be set. We want it to be required. We want it to be secret. And we want it to be like this. So, let's build the package again. Okay. So, it has been built very quickly because we just added another file. We didn't change the old one. So, we can install it. So, it's still not fine. We can bypass that. We can update it. And now, we have this that we just built. It says OSIS UI admin password and the password. And this is what we wrote here or what AI wrote for us. And that was DEF CON. And you can submit that. You can accept that. And remember that we did two things here. We added setup wizard and we fixed the bug about force overwriting the config. So, now when we install, we expect this to be working. And also, we added the UI link. All right. We have UI link. Let's see logs. Okay. Logs are still a bit sketchy because they show password in plain text. What we can do is, for example, if we don't want to do that, if we want password to be a bit more secure, we can, for example, pipe this output to the dev node. And that will hide this output, for example. But since there are no logs, let's see if our UI is working. Yep. Oh, something weird is going on. Let's see what our logs say. Yeah. Thank you. But this is just on my local machine. That's not on the package. Not like that. Yes. So, yeah. There is some kind of login issue. Yeah. Let's try to detect this. Okay. Okay. Okay. Maybe we should have done this. admin.con. Yeah. We are back in. All right. So, we had issues with the HTTPS. That's pretty annoying. And also, your own cloud backup service is not that useful if you can reach it from anywhere. And we have a solution for that. So, as a part of the core system, we have something called HTTPS. So, that's the package that we maintain. And that's made for easily exposing your internal services to the wild, to the internet. And it's made that you do it with the HTTPS always. And how do you do that is you go to your OSIS instance. You go to network. Oh, it's already done. So, because I've done it before, but let's remove it. And what you can do is you can create a HTTPS domain mapping. So, you create a new mapping, and you choose a subdomain. So, we want this to be, for example, OSIS. And which port do we want it to connect to? We saw that port is 9200. And these are optional fields that use HTTP basic authentication for you to be even more secure. But since OSIS has its own authentication, we don't need this. So, we click add mapping. Yeah, I don't know why we got the error, but OSIS mapping succeeded. And we have an error here. Client sent an HTTP request to an HTTP server. So, we have to fix this, and I'll fix it quickly because we are running out of time. First, what we need to do is change the domain name in the OSIS to match our domain name that we are mapping to. That's a limitation of the OSIS itself. So, now when we build the setup wizard, we have this nice UI, but we need an advanced editor for this because we didn't specify the rest of the variables. So, we update here. And in the compose file, we need to change our default OSIS server not to be on HTTP, but on HTTP. So, we need to add here another environment variable that's called SSL insecure. Oh, sorry, SSL insecure equals false. Sorry, SSL insecure equals false. And proxy TLS equals false. And when we build a package like this, it should work with our external domain. So, this disables the TLS of OSIS server, but there is a requirement for access to OSIS to be on HTTPS. So, what I said before is still true. We need HTTPS to access it, but SSL termination can be either on OSIS server or somewhere upstream. But what it wants is SSL for some reason. So, yeah, this is the basic guide how to build a working package. There is a couple more things I wanted to talk about. So, for example, when we are using this SDK, we have tooling that help it maintain. For example, there is automatic tool that does bumps on your GitHub actions that you don't have to follow these upstreams always. It automatically opens your PR and stuff like that. But this is the basic stuff what you need to do when you are building a package. Okay, let's install it one last time. Let's click on advanced options. Let's update it. Accept, accept, and here we go. So, for last bit, let's talk, okay, when we build the package, what do we do? How do we publish it? We use our SDK. We call the publish command, and for some reason, we always have to say what we are bumping to. So, we have either major, minor, or patch version, but since the package is new, that's not important. We can do major, and now this is doing a similar thing, but it will give us a link how to publish our package. Now we have it here installed. I'll delete the volume so we are fresh. So, there is no leftovers from before, and we would expect this to work on our servers, but it doesn't. Yeah, proxy, oh, for some reason, this has not been set properly. Proxy TLS, false. This should be true. Update it. Yeah, I see the logs. Update it. Yeah, here we are, and what's good about this, this is publicly accessible now. I can disconnect from my wire guard, and it's still working. So, you have your own cloud in the, not cloud, but echo. And, yeah, with the, and yeah, the last thing that's left is to show you how the publishing works. You connect to your VPN, you do the release, and then this gives you everything you need to know when you're going to publish. It gives you a link that you can publish it. It gives you a prompt to connect your metamask to sign the release, and if your signature is trusted, it will show up on the UI side. So, I don't have a wallet in Safari, but that's pretty much it. Excuse me? I can repeat the question. Thank you. So, how can publishers find releases? So, I can show you quickly. Let's go here. So, you connect your wallet. We won't use Rabi. We want to watch. So, we will use some testnet wallet. We can connect. I don't know why it's asking me for Rabi. Yeah, I don't want to use Rabi. But, yeah. It's just like ordinary signing process. You find the message, this is my package, this is my wallet, and you sign it, and that signature gets added to IPFS package. And that all gets published on the R repo manager that holds the version and IPFS cache. And when it's sitting on that node, it shows up that sign. Do you have a support channel? Yes. Where? In Discord. Thank you. Any other questions?
