Containers Don't Make Your AI Agent Safe
- Views
- 55.5K
- 55,538
- Outlier
- ×1.2
- Median 47.5K
- Length
- 36:16
- Published
- Jun 30
- Tuesday
- Comments
- 94
- 1.6K likes
Script
How it's written
- Words
- 9,334
- Words a minute
- 257
- Reading time
- 40 min
- Segments
- 26
The hook
0:00–0:25We've all heard the horror stories of AI deleting entire databases or wiping people's entire computers, which is why most people when they run AI make it ask for permission to run anything that could be dangerous to your computer. The problem with this though is that it's very slow, constantly interrupt you, and absolutely kills your productivity, which is why some people just swap over to letting the AI do anything with no permission boundaries at all, which is where we run into these potential problems. On top of that, npm seems to
Structure
We've all heard the horror stories of AI deleting entire databases or wiping people's entire computers, which is why most people when they run AI make it ask for permission to run anything that could be dangerous to your computer. The problem with this though is that it's very slow, constantly interrupt you, and absolutely kills your productivity, which is why some people just swap over to letting the AI do anything with no permission boundaries at all, which is where we run into these potential problems. On top of that, npm seems to have a new security vulnerability every single week with horrible malicious software that'll steal your API keys and destroy your computer. And it doesn't matter if you're using AI or not. It's super easy to fall for these npm packages. Now, this is why I use sandboxes for every single thing that I do. And this is super important, especially for AI development, because it makes sure that you can let the AI run with no permission checks at all, but with the peace of mind that it's impossible for it to do anything bad to your computer or delete any databases or anything like that. Also, it fully insulates you from any type of npm security vulnerabilities because this sandbox is isolated from your entire system. In this video, I'm going to show you how you can set up these sandboxes, how you can customize them, and how you can use them for agentic coding and non-agentic coding tasks. Welcome back to WebDev Simplified. My name is Kyle and my job is to simplify the web for you. And for this tutorial, we're going to be using Docker Sandboxes. Now, this is a tool that I've been using way back when it was in early access, and now it's fully released, which is great because it's much more stable. And I absolutely love this tool so much, I actually reached out to Docker to see if they would sponsor this video, and they were kind enough to sponsor this video, even though, to be
Watch from 0:00completely honest, I was going to make this video either way because I love the tool that much. The main reason I like this tool so much is because it works the exact same. Whether you're on Mac, Windows, or Linux, it all works exactly the same, which makes it super consistent and easy to work with across your entire team. And it's by far, in my opinion, the easiest sandbox to get set up with and work exactly like you want it to. Now, to get started with this, it's super simple. Just go to the getting started page, choose your operating system, and make sure if you go down to the install section, you just run whatever the install command is, that'll download this Xpx tool. And then we can use this to log in and actually get started. So, let's go ahead and do that. I just have a really simple project here. It's essentially entirely blank for the most part. It just has an index html file and it's got a script file. And we can just run that xpx login command. And that's going to go through. It's going to essentially open up Docker. You're going to need to sign into Docker and create an account if you don't already have one. But once you do, you can just click confirm. And you don't have to worry. All of this is entirely 100% free. I wouldn't be recommending it to you if it wasn't. I like it so much cuz it's super easy to use and entirely free. Once you're done with that, you can now go back and you can see we are now authenticated with that account that we created. From here, we can actually go ahead and create sandboxes however we want. Now, before we do that, I want to first make sure we understand the difference between something like a sandbox, a container, and maybe a full-fledged virtual machine. Here we have an example showcasing kind of how the Docker sandbox works. You can see inside here we have the actual code we're running.
Watch from 1:30Often times, if you're doing agentic coding, you're going to have some agent running inside of this. That's going to be running inside of a Docker container. And the nice thing about that is it allows you to just use all your Docker stuff that you're used to. So, if you're used to containerization in Docker, you can use that inside of here or you can ignore it. It really doesn't matter. The most important thing though is it's sitting inside this microVM. That's essentially our sandbox. It's a virtual machine, but instead of a more traditional large virtual machine that's slow to set up and consumes your resources, this microVM is going to consume some resources for sure, but it's much smaller and much quicker to spin up and shut down, which makes it much more seamless to work with. And that's just sitting on top of essentially a layer built into your operating system. So it doesn't matter Mac, Linux, or Windows that communicates with your actual machine. This way, all of this code inside of here and everything running inside your sandbox is isolated from the rest of your machine, which means it's impossible for it to access things unless you explicitly give it permission to access those things. And that gives you that security because now it can't access your credentials. It can't go out to the database on your web and delete that. It has to only do the things inside the sandbox that you gave it permission to do. So, now that we understand what a sandbox is, you can hopefully kind of already start to see some of the benefits. Now, the nice thing about these sandboxes is Docker gives you some really easy defaults you can use. So, if we want to start up a brand new sandbox and let's say we wanted to do a sandbox using GitHub copilot because that's what I have a subscription for. You can just type in copilot here and make sure we type run before that because we want to run a brand new sandbox. We're going to call copilot saying we want to use the co-pilot agent essentially inside of here. And that's going to set up that brand new sandbox for us. Let's also give it a name so it's easy for us to spin this up and down. So, we'll just
Watch from 2:50give it a name of copilot test. Just like that. And as you can see, this is going to start it up. And the very first time you run anything inside of Docker, it's essentially going to ask you here whether or not you want to have different network policies. And this allows you to make sure that when you are calling out to the web, it's going to restrict what domains you have access to. So what we want to do is set what we want in this. So we have open, balanced, and locked down. Open essentially allows all network traffic. There's no restrictions at all. I'd recommend not doing this. Balanced is a great middle ground. defaults to denying any request that you send, but a lot of common sites like npm and so on that you're going to use to download packages are going to be allowed by default. And then lock down just essentially blocks everything unless you explicitly allow it. I generally think balanced is a good approach because it gives you all of those different dev dependencies like npm that you're used to, but it blocks other things so that you can't go out and accidentally download malicious code from the AI. It's going to completely block that. Or if it tries to access your database and drop it, it can't do that because it doesn't have access to that web layer to do everything. Now, the very first time you run this, it's going to take a while because it has to download literally everything for your entire sandbox. But from then on, once you run this, it's going to be much quicker because it already has everything downloaded. It just has to spin it up and spin it down. So, it looks like it just finished running. And we should hopefully see here that we have that Copilot prompt set up. And now we're essentially directly inside of Copilot. Now, it recommends running this login command to sign into Copilot. But actually that's not what I want to do because that's going to essentially put my credentials inside this sandbox which means any code running in here has access to those credentials. That is one of the things I want to prevent because npm packages will try to steal your credentials from your environment variables. So we want to make sure we
Watch from 4:20avoid this. So I'm going to exit out of this sandbox completely. And now what I want to do is I want to actually inject my credentials into that sandbox in a way that is secure so the sandbox doesn't actually know what those credentials are. Now to do this with Docker sandboxes is incredibly easy. I can just run spx and then I want to say that I want a secret and I want to set that secret. And then we can specify what secret we want to set. In our case, I'm going to do a global secret and we're going to call this one GitHub. This is a predefined name built in. There's lots of them for like Anthropic and all the other major AI producers that you have. They're going to have the ability for you to set a secret key. And when I hit enter on this, it's just going to ask me to enter in whatever that key is. Now, I could type in what that key is manually, but there's also an easier way for me to get around doing this. If you have the GitHub command line tools installed, what you can do instead is you can run echo. And then we just want to wrap this in some parenthesis here. So we can say get our quotes. There we go. And then inside here, I want to essentially run a command which is just GitHub off token. If you have the GitHub command line tools installed already, this is essentially going to take your token and it's going to output that token and with this pipe symbol, it's just going to push that token in. This is exactly the same as if I manually type that in, but I don't have to actually type that in and show you my GitHub off token. Instead, when I run this, it's going to take that command, it's going to run it to get that GitHub off token, and then it's going to save that in that secret credential environment variable. Now, if I go ahead and I actually just try to run that co-pilot and give it the same exact name again. And of course, since we've already actually created this, I just need to run it by passing the name.
Watch from 5:52So, I can just say run and then whatever the name of the sandbox is we created. And that's going to open up my sandbox. And you can see how quick it is the second time you open it. Now, the problem is though, since we just created a brand new environment variable, it doesn't have access to that environment variable because we created the sandbox before that environment variable existed. So, you can see I still don't have access to any of my models and it's asking me to log in. We can get around this by just essentially deleting the sandbox and recreating it. And now it's going to pull in those environment variables. And the nice thing is is once you set your environment variable once, it'll have access to it forever. So, you don't have to worry about doing this normally. So, what we can do is we can say spxrm. That's going to remove the sandbox. and we give it the name of copilot test cuz that's what we called this. And then it's going to essentially say, do you want to delete that? Yes, we want to delete that. And it's going to go through and it's going to delete everything related to that sandbox. This now means that we essentially need to recreate that sandbox. So we can just come in here, use that same run command to create a brand new sandbox. It's again going to take a little bit of time to start up your sandbox, but as you can see, it's relatively quick. And the nice thing is it actually has that credential injected inside of it. So now I can do whatever I want. For example, I can say say hi. And it's going to actually prompt the AI and I get back the response of hi. And the nice thing is is if I try to access that GitHub off token, I can't. For example, if I just do echo dollar sign GitHub token, that is the GitHub token. And when I hit enter, you can see this is not the actual token. You can see spx proxy managed blah blah blah blah blah. That's essentially saying that the container has no idea what the actual token is.
Watch from 7:18The token is stored outside of this sandbox. And whenever a request is being made from the sandbox, what happens is the request is made. the request goes outside of the sandbox and then outside the sandbox this credential is injected in and replaced with the correct credential before it gets to GitHub. So essentially there's a middleman that intercepts my request from the sandbox, adds that credential in and then lets it go across to the GitHub servers. This means it's impossible for any malicious script or anything that goes on in your code to access your credentials when you don't want them to. This is one of the big benefits of using this whether you're doing agentic coding or not. It keeps your credentials entirely safe on your system since there's no access available inside the sandbox. So, if I downloaded a malicious package on this exact sandbox, it doesn't matter because it can't access my credentials or do anything to my system because it's entirely locked down. Speaking of, if I were to come in here and I just try to access various different files, for example, let's just try I cd into the root directory and then I want to run here ls. You'll notice that it only has access to like a few different folders and files and all of this is inside of its own container. You can see the path here, g webbimplified current project. This is just essentially a copy of what is in my folder right here. So these files are being copied over. That's why you can see them here. But everything else on my system is entirely locked down and inaccessible from this workspace because it's all just these specific files and nothing else. To show you what I'm talking about, we could really simply just say delete script.js.
Watch from 8:40And this will actually go through. It'll delete that script.js JS file because it has access to that particular file. And if we were to come over to here, you can see that file has been removed just like we would expect it to. But if I have a file somewhere else on my computer, it is entirely impossible for it to access that. So let's just go ahead. We're going to exit out of here and I'm going to create a brand new folder. We'll just call this test. And inside of here, I'm going to move that index html. And outside, I'm going to create a script.js. So you can see I have this test folder with index html inside of it and the script folder on the outside of it. Now, what I'm going to do is I'm going to CD into that test folder just like that. And I want to start my workspace here. So, I want to create a brand new sandbox. We're going to call this C-pilot nested just like that. That's going to start up a brand new co-pilot sandbox inside this test folder, which means it only has access to the code inside this test folder and nothing else at all. So, if we just come in here and I were to run that ls command, you can see all we have is index.html that's in that test folder. And if I were to try to cd up a level for example, and then I wanted to run my ls command, you'll notice it doesn't have access to anything else, you can see there's this agent MD file that's automatically added by Docker, essentially telling our agent that it's inside of a sandbox. But you can see this script file doesn't exist at all. And if I try to delete this, I can just say delete. And let's just copy the full path to this file and see if it'll delete it. We'll say copy path.
Watch from 10:03Paste that in. And when I hit enter, it's not going to be able to find or delete this at all because it doesn't have access to this file on my system anywhere. And no matter what it tries to do, you'll notice that this file is always going to be here no matter how many times I refresh. You can see it says it deleted it cuz it tried to run the command to delete it. But it doesn't have access to that file. So the file is still 100% here, not deleted at all. Now, this alone is already enough reason to use sandboxes in my opinion cuz it gives me that peace of mind. I can run my AI without any safety checks and I can know that it won't mess up anything on my system at all. But obviously sometimes I want to take this a step further because right now it can still delete files inside this folder. It can add things into those files. It can inject code into like GitHub hooks and things like that. So it can still technically cause me problems by adding files into this folder. And then if I run code, I could maybe run malicious code from these files. So there's one step of problem there. So if I wanted to be able to prevent that problem, what I can use is essentially something similar to GitHub work trees by cloning this entire thing instead of having it essentially linked to this. Because right now the way it works is every time I make a change inside of my sandbox, it duplicates that change outside my sandbox right here only for the files that it has access to. This is great because it's super easy to work with, like I said. But again, you could run into potential security problems. If you have malicious code that adds like a GitHub pre-commit hook that does something malicious, that pre-commit hook will get added to your code and then if you try to commit something in GitHub, it'll run that. So if you run that code on your normal machine instead of in the sandbox, you could run into potential problems. So, let's go ahead and look at the alternative we can use, which is going to be using a clone to clone this entire repository instead.
Watch from 11:20Now, I'm just going to CD back to that main root level. Just like that. So, now we're at that root level we were at before. And what I want to do is I want to essentially run a brand new sandbox, but this one I'm going to be using that clone mode instead. So, I can just say d- clone just like this. That's going to allow me to run this as a clone. And then I can specify that I want this to be copilot. And I can specify the name. We're just going to say copilot clone just like that. And we are immediately getting a warning saying that you must have a git repository to do this because it works off of git work trees. So we're just going to say get init to create a repository. There we go. And now we can run that clone command instead. And the big difference between the clone command and the original command is that instead of just having essentially these files be modified directly between the two. What happens is it actually duplicates all these files into its own work tree that's entirely separate. And then if I were to make changes, for example, I can just say add a styles.css CSS file with nothing in it. There we go. So, it's just going to create a blank styles.css file for me. And you'll notice it doesn't actually show up over here at all. You can see no matter how many times I refresh, this file does not show up over here. While if I actually check inside of here, you can see there's that styles.css file right there. Also, you notice none of the rest of these files are showing up. And that's because I need to make sure I add those files into my repository for them to show up.
Watch from 12:50Because when you do a clone, it only clones whatever the current branch is at. So, I need to have those committed into my branch. And since there was nothing committed to my branch, nothing is going to show up. So let's go ahead and actually fix that by just saying get add. And we'll just commit these files. There we go. So now we have those created. And to get this working properly, what I want to do is I want to remove that copilot clone sandbox that we created. And this one, you'll notice we get a much longer warning message. The reason for that is because when you delete a cloned sandbox, all your files that are changed inside of there, unless you pull those down to your main repository, they will be deleted as all. So that's why it gives you a longer warning message. But now let's go ahead and we want to create this again. So let's just find that create command right here. That's going to clone everything. And now since we have these committed to our actual repository, they will show up inside of our list. So if we just give that a second and I run that ls command, we should see all those different files just like we expect. And now I can say create a blank styles.css file. There we go. It's going to go through. It's going to create that file. And again, that file won't show up over here because it's only inside of this specific git work tree. If I want to access that, I essentially need to pull that code down just like I was pulling down a pull request from GitHub. So, let's open up a brand new terminal. This terminal is inside my actual non- sandbox environment. And now what I can do is I can actually pull those different things down. So, I can say get checkout. And if I just do a quick search here, and actually let's do get fetch. And you'll see right here, sandbox copilot clone. The name of this is always set to sandbox- whatever the
Watch from 14:06name of the sandbox is. That's going to allow me to pull down the code that comes from that specific sandbox. Now that I've pulled down that sandbox code, I can come here with a get checkout. And if I look at my options, you'll notice I have the options for the code from that clone. So, let's just copy down that clone just like that. And now that's pulling down the code from that section. Now, the problem is is that I didn't actually commit the changes over here. So, I should just say commit and add those changes. There we go. That way, it's going to make sure all those changes exist inside the repository. So, when I pull it down, they will be accessible. So, now it committed all those different changes. So, now if I were to just do a quick fetch here to fetch all that down, and I were to check out that branch, you can see that blank styles.css is right there like I expect it to be. So, this essentially allows me to work in a way that I know that all of the code that is inside this section, if I'm pulling it down, I know that it could have potential problems inside of it. I should review it as if it was a pull request coming from somewhere else. Now, this again doesn't protect you if you were to have malicious code inside of here and you run it. Obviously, that's your own problem that you ran into yourself. But this gives you that extra level of security cuz these files will never be modified unless you manually pull down the code. Now, the downside of this is it's much more cumbersome to work with. So, for the most part, I just work inside of noncloned environments because it's easier to work with. But if I had some more security concerns or maybe things like pre-commit hooks and so on that were running often, I would maybe think about doing this in a slightly different way. Now, the next thing I want to talk about is going to be security policies that we can specifically use. So, let's go into our copilot. I just want to clear this out.
Watch from 15:37We'll just restart this from scratch again. So, let's just get that running just like that. That's going to be that cloned version. And what I want to do is I want to show you how it doesn't have access to like the web by default. It has access to some sites but not very many. For example, I can say, what is the latest ESLint version according to the docs? And I'm just going to paste in a URL to those docs. And when I run this, it's not going to be able to access these docs. It's going to get an error. As you can see, fetch failed. And that's because by default, it's locking down all of those different external API routes. And it's not letting it access the internet except for on very specific pages where I told it it has that access. If we want to be able to modify this, for example, I want to be able to access that ESLint website, we can modify the policies that we have. So, if we say SBX policy and I type in network, that's going to give me all of my different network policies. And specifically, we want to list them. So, let's do policy ls and then we'll type in network. And this is going to show all of the different things that are currently being allowed inside the network. So, you can see all of these are things being allowed by that kind of basic profile we set up that allows it to access lots of different things that you would normally need to access. for example like Debian and so on to download various different things. You can see like forcel and Google inside of here for common development related things Figma and so on. If we want to be able to add to this, it's actually quite simple. I can just type in SBX policy allow. This is going to be a network policy and I'm going to make it global which means any sandbox has access to this. But if you want to restrict it to a specific sandbox, you could type in a specific sandbox name. And then we just need to put whatever that domain name
Watch from 17:07is. Now in our case we want this to be eslint.org. So we can just paste in eslint.org and that means any request to eslint.org is going to be acceptable. So now we can click enter on that. That rule has now been added as a local rule on my particular computer. And now if we were to come into here, let's just close out of this and restart it just to make sure that it gets that brand new network policy. And if we were to run that exact same command, we should hopefully see that it is able to correctly fetch this documentation. You can see it fetched it. It was able to successfully fetch. There are no errors and it's given me the exact latest version. So that's how you can modify your different policies for what you can allow and deny access to. And if you wanted to, for example, deny access to something, you could just say deny in here, and that's going to explicitly add this as a denial rule. But oftent times, if let's say I want to remove this eslint.org that I added. All we do is just do a list command. You can see right here, this is what I typed in as the thing that I allowed. All we need to do is just say spx, we want to do a policy. So we can say policy. RM is going to allow us to do a removal. This is global, so we'll pass in hash-g. And then this is the resource that we want to remove. So we'll say resource. And then whatever this is, eslint.org, we'll just copy that over. And we'll hit enter. And we of course need to make sure that we specify that this is a network policy. That was a small mistake. There we go. And now it's going to remove that. You can see rule removed. And if we were to do a search for that, you can see it no longer shows up in our list. And again, if we were to try to make a request, it's going to be denied trying to make that request because it doesn't have access to that specific thing. It looks like it's just regurgitating what I had before, but you get the idea. It would not be able to
Watch from 18:39make that request because I removed that from my network policy. Now, the next thing I want to show you is how you can create your very own custom sandboxes. Cuz by default, if we look at the agent supported, you can see it's got pretty much all the big ones. But one that you'll may notice is not supplied is Pi. There's no PI support. But the nice thing is is they have this shell sandbox. And the shell sandbox is just an empty shell. It's all it is. It's just an empty shell that you can log into. It's just like sshing into like a server, for example. That's what you have access to. And you can also customize what these empty shells will look like by creating different templates. So we look at this customize section. You can see there's this whole section on kit and that allows you to essentially create your very own custom sandboxes. And I'm going to show you how you can create one for PI that we can also use to work with local AI models. So we can have a fully sandboxed local AI setup using PI, which is incredibly powerful. Now to get started with creating these custom sandboxes, all we need to do is we need to create a folder. We can call it whatever we want. We're just going to call this one PI. And inside that folder, we need to create a spec.yaml file. And this is where we define essentially everything for our agent. And by default, we can copy over from the documentation the starting point for the cloud agent. I'm going to paste that in. Don't worry, I'll explain what everything does because a lot of this we don't even need for our PI setup. But this is what the cloud setup looks like directly from Docker. You can see that there's a Docker sandbox template for the Cloud Code Docker image. And then there's a bunch of other configuration that essentially allows Cloud Code to be installed and run directly inside of this. Now, in our case, since we're using a custom shell, we don't want to use the cloud code docker image here.
Watch from 20:09So, instead, we're going to use the shell Docker image. What that does is essentially allows us to use a completely blank sandbox with nothing in it, but it does have Docker installed, which means we can run Docker inside of our containers if we want to. So, that way we can use Docker in our sandbox. Now, the next thing we need to do is up here we have our schema version. Right now, it's a version of one, so we'll leave it as one. We're creating a brand new agent, so we'll call this agent. And the name here is what we call from the command line. So when I was doing in my command line the different spx run copilot for example, this would now be replaced with pi because that's what I called the agent in this name section. Then inside of here is where we define where all of our different agent related code is. So our AI file name for this is just agents.md. Claude's the only one that doesn't support this, but we're going to be using the agents.md. We'll leave persistence as persistence. So it kind of saves a volume with all of our memory. And then the entry point, this is just the command that gets run from the terminal. So in our case, all we need to do is just run pi. So we can put pi inside of here and this will run the pi command as soon as our thing starts up. So it'll essentially start up a server and it'll run pi. That way we're immediately inside that pi terminal. Now there's an entire section on network and this is just saying what network connections are being allowed to be made. Now in our case for pi we only need to allow pi.dev. That's because whenever you start up pi it's going to reference out to pi.dev to see if there's a new version to be installed and if so prompt you to install it.
Watch from 21:32That's the only reason we're adding this in here. Otherwise, we don't need any other allowed domains. And this entire section here for service off and service domains. Since we're using PI as a local AI setup, we don't need any of this cuz all of that is for saving credentials and making sure your credentials are properly sandboxed and not accessible inside your image. But again, we're using this for local AI. So, we don't need that section. Same thing here with credentials. We don't need this because we're not using any credentials because we're using entirely localbased AI. Now, if we wanted to, we could leave this is sandbox as one. Doesn't really matter. That's just a key that we can have in there so we know we're in a sandbox environment. But I'm going to be adding in the pi coding agent directory. I'm going to set that equal to the home directory right here. And then essentially it's going to allow me to do is by default when you pass files into your actual new sandbox, they're going to be put in a specific home folder. And I want to make sure that pi uses that exact same home folder. That way it can sync up the files being created by our sandbox. This will be useful when we start talking about adding in that local setup as well. Finally, the last section is commands. And this is just what command runs when you install and set up your sandbox for the first time. So we need to obviously install Pi on our computer to be able to use it. So what this command is going to do is we can just copy paste this entire thing with the npm install command for PI and that's going to go through and it's going to install PI. If we set the user to 1,00 that's going to be using the agent level user. So the user can either be set to zero or th00and. Zero is like the root user and a th00and is a slightly more agent level user that has its permissions slightly revoked. So, I'd recommend using user 1000 in pretty much every use case you need. Next, we
Watch from 22:51can just give this a name, install PI, and that's it. That's literally all it takes to set up our own PI agent. For the most part, all you need to do is set up your image as a blank shell docker. Then, you can specify your entry point, specify your install command to install that, and then if you need to set up any environment variables or network related stuff, you can do that right here. That's it. Now, what we can do is we can run that. So, I can't just run sbx run pi. If I do that, it's going to say, hey, pi is not an actual sandbox. So instead, we need to reference a kit. So we can say d-kit and then pass it the directory to where this kit lives. Now it lives in this pi directory right here. So we're going to pass it that pi directory. And now when I run this, you're going to notice it looks like we're actually getting an error. It says line 7, this field persistence is not found. So let's just go ahead and remove that. This could be that I'm running on a slightly older version of the Docker sandbox or things like that. This kit section is kind of experimental still. So things are changing rather rapidly in this space. But now if we actually run this, we should see it's creating a brand new sandbox. It's going to download all the different things that it needs. It's also going to run that command which is right here, pi. And it's going to run the install command right here. And once it's done with all that, we should be booted up directly into Pi. And as you can see, we are now inside of a PI terminal. Right now, I don't have any models or anything set up, but I am in a PI terminal. So, we at least know that this is working just like we expected to, which is great. So, now we can create any agent we want. And all we need to do is add in that local model support. Now, in my case, I'm going to show you how to set this up using LM Studio. As you can see here, I have LM Studio running and it's running on this exact local host port. So, if you want to learn more about how you can set up LM Studio from scratch to run local AI models, I have a full video.
Watch from 24:22I'll link it in the cards and description for you. But for now, I'm going to assume you have LM Studio running with various different models on it. The next thing that we need to do is we need to make sure that we can access local host. So, we need to expose a network that we have access to. In our case, we want to access that local host network, which in our case is 127.0.0.1 at port 1234. Now, I'm just going to change this to local host. I find it a little bit easier to read and work with. There we go. So, now we have our new allowed domain of local host. The next thing that we need to do is we need to make sure that we have all the files for our model setup because the way that you set up local models in PI is you essentially create a JSON config file for those models and those live in the home directory right here that we just specified with this environment variable. So, let's create a brand new folder. We're going to call this folder files and this is built into the naming of creating these kits. So if we create a folder called files and then inside that folder we create one called home. Essentially every single file we put in this home directory is going to be added to the home directory of our agent when we set it up and run it. So this is essentially just going to copy paste those files automatically for us. So let's create a brand new file in here called off.json. And we also need to create one called models.json. Just like that. And for this off.json I'll just paste in the code. This is just what you need for LM Studio. You just need to give it a name. We'll call it LM Studio. The type is an API key and the key it doesn't matter what it is because you're using this locally. There is no real API key. So we can just paste in any value for key. It doesn't matter.
Watch from 25:54This just tells PI we now have a new provider called LM Studio. And the way it connects is using this key called key. But again there's no key. It's fully local. So it doesn't need to worry about that. The next more confusing part is going to be our models. I will again copy this over and explain what it is because it's mostly just going to be different configuration based on what model you're using. So here we have our providers and then on our providers we have LM Studio. This just must match whatever name you put in your off. From there, we then specify our base URL. And you'll notice instead of putting local host here, what you normally would, I'm instead using this docker container here. So, I'm using host.docer.in. That just tells docker point towards local host. So, since we're running this in docker, instead of writing local host, we write host.docer.in internal. And that allows docker to properly send this to my computer instead of running it inside the sandbox localhost. Next, we need to specify the API as open AI completions. Again, our API key doesn't matter. We can just put that as the same thing we have over in our off. And then finally, we can me specify what our models are. So, this ID just again comes from LM Studio. Whatever the name of the model you're using is, put that as your ID. Put whatever your context window is, whether or not it supports reasoning and also what input modes it supports. And you can add as many or as few models as you want inside this section. And with that, that's literally everything we need to do to get this set up properly.
Watch from 27:17Now, we have these files. they will be copied over automatically when we create a brand new sandbox. So what we need to do is first delete the sandbox that is currently running. So we can say xbx ls to see what our sandboxes are. You can see there's one called pi current project. So let's just remove that one. Just like that. That's going to delete it. And then we can come in here and we can essentially create our sandbox. So let's just run that sandbox again. That's going to create a brand new sandbox using this kit. But since we added these off.json and model.json files, these are going to be automatically copied over for us. And you should see once we get this up and running that actually is running and we're connected to that Quen model. And I can just say say hi. And it's going to take a while for the very first prompt to work just because I'm using a local model. It needs to warm up and load that in my GPU. But once it actually loads, it'll respond to us with that message. And from then on, it'll be much quicker. This is really just a problem with the startup speed of a local model. But again, this is just how local models work. And if you again want to learn more about how to set up your own local model, I'm going to link that video in the cards and description for you. It's a very in-depth process, but it can save you a ton of money. And like I said, it's normally not this slow on each request. This is just the very first request, and I'm recording video at the same time. You can see follow-up requests are so much quicker. Now, another thing that's really nice about how these sandbox environments works is it doesn't copy over any of your global stuff. So, you'll notice inside of Pi, there's no skills or extensions or anything loaded because I don't have any of my global stuff. Everything is just sandboxed inside this environment. And maybe you want to have some skills that you use in various different environments. Maybe you want to use the same skills in Pi and Copilot and Claude Code and so on. So, I'm going to show you a way that we can set that up as well. So, what we can do is we can create a brand new folder. I'm just going to call this one skills.
Watch from 28:32And inside here, I want to create a spec.yamel. So, let's just copy the one we already have for Pi. And we'll paste it into here. But, we're going to modify this drastically because instead of setting up an agent, we're going to be setting up a mixin. You can almost think of this like a plugin or an extension. Essentially, it's a bit of code that I can run every single time. And I can add it to any sandboxed environment that I want. It doesn't matter. And this allows me to add this to my PI terminal. It allows me to add it to cloud code, copilot, or anything else. We'll just call this skills just like this because I'm going to use skills as an example. And since we're not using agents, we can remove that agent section. We can remove the network. We can remove the environment. Really, all we care about is a command that is going to install these skills for us. So, I'm just going to copy over the command for installing a skill right here. And I'll explain to you what we need this command to do. So, first we're calling npx skills add, pasting it in the URL for our skill. And then to make sure this works properly, specify the skill you want. In our case, we're just using the default fine skills. We can make sure that it's a global install. And if we add dashy, that's going to make sure that it properly just runs through and sets everything up without us having to do user input. And then we can just call this install skills, whatever we want. This is going to just install one skill, but you can add as many or as few or different extensions and so on inside here. And again, make sure your user is set to 1,00 to make sure that it's scoped down to the agent and not the root level. Now, we can add this directly into our sandbox if we wanted.
Watch from 30:04So, let's come into here. Let's make sure that we clear this out so we have a fresh slate to work with and we have our running of our pi kit here and instead I want to add to this pi kit. So to do that let's just say add and then we need to get the name of this project which I believe is just called current project pi. I think that is the name that it defaults to. And then we need to specify the location of our kit which is just that folder called I believe skills. There it is. And now if we click run on this that should hopefully try to add that. And it looks like I had a problem. And that's because I need to make sure I call kit add so it knows to add this kit directly in. So now if we say kit add looks like my name is incorrect. I believe it is pi current project. There we go. Now that should go through and it's going to run all the commands and everything. You can see it's running that command right now. And now it has added that directly into my project. So what I can do is I can just start up that project that we have which is pi current project. Just like that. And of course I need to make sure that I have the kit associated with it so it knows how to run pi properly. So I can say kit/py. There we go. Now that's going to run this. And you can see we have that scale installed. So this allows me to dynamically add content to these various different platforms however I want. So we can have a full agent-based kit right here which is great if we want to set up essentially our very own PI or other agent. And we can add things like skills or if you want to install Python or various different packages. All that can be used in a mixin which you can add to any of these you want. And the nice thing is is you can actually specify these and save them in GitHub. So they don't need to be stored locally like this and you can just download them
Watch from 31:20directly from GitHub. To show you an example of what that looks like, I just actually have some sandbox examples here. I have pi, I have skills, and then I have the pi lm studio. And I want to show you how you can run these directly instead. So all you need to do is just take this URL that you get. We'll use the https URL cuz it's the easiest. We'll copy that URL to our clipboard. And then what we can do when we want to run our project, let's just again make sure we clear this. I want to run and I want to set up a new one. So let's make sure we remove the current one. So we'll just say rm pi current project. That way we have a completely blank slate to start with. Give that a quick second to delete. And then we can run the run command to be able to start up a new one. So we can say run. I want to run pi. And instead of passing in my folder of pi for my kit, I'm going to instead say kit, I'm going to take that entire URL. I'm going to paste it into here. And then for this URL, I want to make sure at the very start I put git with a plus afterwards. That way it knows it's accessing git. And since I have a folder called pi inside of here that I want to use, I can just make sure at the end I put the pound symbol. I type in diir for directory equal sign and then whatever that folder name is, in our case pi. That's going to essentially reference this skill directly or not skill this kit directly from GitHub. And it's going to set up pi using that exact kit. And again, if we give it a second to run, we should see that we have pi set up directly inside of our terminal and it's coming from that GitHub repository. You can see I have pi. None of the models or anything are set up because this version on GitHub has the models separated, which is great because now we have all this information that we want. And another really great thing is I can add multiple
Watch from 32:51kits. Let's say that I wanted to add another kit onto this particular section. And this kit is going to be copied from that exact same URL. So, we'll just copy this URL here. Paste that down. Make sure I fix all the formatting that got messed up. There we go. And instead of having that PI directory, I instead want this to be the PI LM Studio directory. There we go. And now this allows me to set this up using two kits at the same time. And actually, let's just make this one the skills kit. That way, we can test this with our skills properly. Now, this will fail because I already have that agent running. So, let's just do spx rm pi current project to delete the one that currently exists. That way, we again get a fresh slate for testing this. And now, it's going to run both of those skills at the exact same time. And it looks like my removal maybe failed. Let's make sure that I actually remove this properly. There we go. And now we can run that and it'll set up. And again, it's going to go through and run both of these different kits. The first one is going to set up PI and the second one is going to add that skill in there. And we should see we have a PI setup with no local models, but it should have that skill installed for us. Now, if we give it a quick second, you can see there's that skill and none of the local models. So, that's how we can set up all these different kits, store them on GitHub, and use them however we want. And these kits that I have created, the ones that we made in this video and so on, I'll link down in the description so you can access them and use them however you want. Now, there's a ton more that you can do with Docker Sandbox, and I can't cover everything in this video, but this is 99% of what I use on my day-to-day basis with Docker Sandbox, and I honestly don't write any AI code unless it's inside of a sandbox from Docker Sandbox. I really want to give a huge thanks to them for sponsoring this video because I was pretty much going to
Watch from 34:23make this video anyway, so them sponsoring is just a huge support for the channel. And if you want to check it out, it'll be linked in the description below. It's again entirely free for you to use, and I highly recommend it because it makes your system so much more secure than if you didn't use it. And it will make you more productive as well. So you you don't have to babysit the AI and make sure it does everything properly. So with that said, please check out Docker Sandbox link down in the description below.
Watch from 35:54Full transcript
- 0:00
We've all heard the horror stories of AI deleting entire databases or wiping people's entire computers, which is why most people when they run AI make it ask for permission to run anything that could be dangerous to your computer. The problem with this though is that it's very slow, constantly interrupt you, and absolutely kills your productivity, which is why some people just swap over to letting the AI do anything with no
- 0:20
permission boundaries at all, which is where we run into these potential problems. On top of that, npm seems to have a new security vulnerability every single week with horrible malicious software that'll steal your API keys and destroy your computer. And it doesn't matter if you're using AI or not. It's super easy to fall for these npm packages. Now, this is why I use sandboxes for every single thing that I do. And this is super important,
- 0:41
especially for AI development, because it makes sure that you can let the AI run with no permission checks at all, but with the peace of mind that it's impossible for it to do anything bad to your computer or delete any databases or anything like that. Also, it fully insulates you from any type of npm security vulnerabilities because this sandbox is isolated from your entire system. In this video, I'm going to show you how you can set up these sandboxes,
- 1:01
how you can customize them, and how you can use them for agentic coding and non-agentic coding tasks. Welcome back to WebDev Simplified. My name is Kyle and my job is to simplify the web for you. And for this tutorial, we're going to be using Docker Sandboxes. Now, this is a tool that I've been using way back when it was in early access, and now it's fully released, which is great because it's much more
- 1:22
stable. And I absolutely love this tool so much, I actually reached out to Docker to see if they would sponsor this video, and they were kind enough to sponsor this video, even though, to be completely honest, I was going to make this video either way because I love the tool that much. The main reason I like this tool so much is because it works the exact same. Whether you're on Mac, Windows, or Linux, it all works exactly the same, which makes it super
- 1:42
consistent and easy to work with across your entire team. And it's by far, in my opinion, the easiest sandbox to get set up with and work exactly like you want it to. Now, to get started with this, it's super simple. Just go to the getting started page, choose your operating system, and make sure if you go down to the install section, you just run whatever the install command is, that'll download this Xpx tool. And then we can use this to log in and actually get started. So, let's go ahead and do
- 2:03
that. I just have a really simple project here. It's essentially entirely blank for the most part. It just has an index html file and it's got a script file. And we can just run that xpx login command. And that's going to go through. It's going to essentially open up Docker. You're going to need to sign into Docker and create an account if you don't already have one. But once you do, you can just click confirm. And you don't have to worry. All of this is entirely 100% free. I wouldn't be
- 2:25
recommending it to you if it wasn't. I like it so much cuz it's super easy to use and entirely free. Once you're done with that, you can now go back and you can see we are now authenticated with that account that we created. From here, we can actually go ahead and create sandboxes however we want. Now, before we do that, I want to first make sure we understand the difference between something like a sandbox, a container, and maybe a full-fledged virtual machine. Here we have an example
- 2:45
showcasing kind of how the Docker sandbox works. You can see inside here we have the actual code we're running. Often times, if you're doing agentic coding, you're going to have some agent running inside of this. That's going to be running inside of a Docker container. And the nice thing about that is it allows you to just use all your Docker stuff that you're used to. So, if you're used to containerization in Docker, you can use that inside of here or you can ignore it. It really doesn't matter. The most important thing though is it's
- 3:06
sitting inside this microVM. That's essentially our sandbox. It's a virtual machine, but instead of a more traditional large virtual machine that's slow to set up and consumes your resources, this microVM is going to consume some resources for sure, but it's much smaller and much quicker to spin up and shut down, which makes it much more seamless to work with. And that's just sitting on top of essentially a layer built into your operating system. So it doesn't matter
- 3:27
Mac, Linux, or Windows that communicates with your actual machine. This way, all of this code inside of here and everything running inside your sandbox is isolated from the rest of your machine, which means it's impossible for it to access things unless you explicitly give it permission to access those things. And that gives you that security because now it can't access your credentials. It can't go out to the database on your web and delete that. It
- 3:47
has to only do the things inside the sandbox that you gave it permission to do. So, now that we understand what a sandbox is, you can hopefully kind of already start to see some of the benefits. Now, the nice thing about these sandboxes is Docker gives you some really easy defaults you can use. So, if we want to start up a brand new sandbox and let's say we wanted to do a sandbox using GitHub copilot because that's what I have a subscription for. You can just type in copilot here and make sure we
- 4:07
type run before that because we want to run a brand new sandbox. We're going to call copilot saying we want to use the co-pilot agent essentially inside of here. And that's going to set up that brand new sandbox for us. Let's also give it a name so it's easy for us to spin this up and down. So, we'll just give it a name of copilot test. Just like that. And as you can see, this is going to start it up. And the very first time you run anything inside of Docker,
- 4:27
it's essentially going to ask you here whether or not you want to have different network policies. And this allows you to make sure that when you are calling out to the web, it's going to restrict what domains you have access to. So what we want to do is set what we want in this. So we have open, balanced, and locked down. Open essentially allows all network traffic. There's no restrictions at all. I'd recommend not doing this. Balanced is a great middle
- 4:48
ground. defaults to denying any request that you send, but a lot of common sites like npm and so on that you're going to use to download packages are going to be allowed by default. And then lock down just essentially blocks everything unless you explicitly allow it. I generally think balanced is a good approach because it gives you all of those different dev dependencies like npm that you're used to, but it blocks other things so that you can't go out and accidentally download malicious code
- 5:08
from the AI. It's going to completely block that. Or if it tries to access your database and drop it, it can't do that because it doesn't have access to that web layer to do everything. Now, the very first time you run this, it's going to take a while because it has to download literally everything for your entire sandbox. But from then on, once you run this, it's going to be much quicker because it already has everything downloaded. It just has to spin it up and spin it down. So, it looks like it just finished running. And
- 5:29
we should hopefully see here that we have that Copilot prompt set up. And now we're essentially directly inside of Copilot. Now, it recommends running this login command to sign into Copilot. But actually that's not what I want to do because that's going to essentially put my credentials inside this sandbox which means any code running in here has access to those credentials. That is one of the things I want to prevent because npm packages will try to steal your
- 5:49
credentials from your environment variables. So we want to make sure we avoid this. So I'm going to exit out of this sandbox completely. And now what I want to do is I want to actually inject my credentials into that sandbox in a way that is secure so the sandbox doesn't actually know what those credentials are. Now to do this with Docker sandboxes is incredibly easy. I can just run spx and then I want to say that I want a secret and I want to set
- 6:10
that secret. And then we can specify what secret we want to set. In our case, I'm going to do a global secret and we're going to call this one GitHub. This is a predefined name built in. There's lots of them for like Anthropic and all the other major AI producers that you have. They're going to have the ability for you to set a secret key. And when I hit enter on this, it's just going to ask me to enter in whatever that key is. Now, I could type in what that key is manually, but there's also
- 6:30
an easier way for me to get around doing this. If you have the GitHub command line tools installed, what you can do instead is you can run echo. And then we just want to wrap this in some parenthesis here. So we can say get our quotes. There we go. And then inside here, I want to essentially run a command which is just GitHub off token. If you have the GitHub command line tools installed already, this is
- 6:51
essentially going to take your token and it's going to output that token and with this pipe symbol, it's just going to push that token in. This is exactly the same as if I manually type that in, but I don't have to actually type that in and show you my GitHub off token. Instead, when I run this, it's going to take that command, it's going to run it to get that GitHub off token, and then it's going to save that in that secret credential environment variable. Now, if I go ahead and I actually just try to
- 7:12
run that co-pilot and give it the same exact name again. And of course, since we've already actually created this, I just need to run it by passing the name. So, I can just say run and then whatever the name of the sandbox is we created. And that's going to open up my sandbox. And you can see how quick it is the second time you open it. Now, the problem is though, since we just created a brand new environment variable, it doesn't have access to that environment variable because we created the sandbox before that environment variable
- 7:33
existed. So, you can see I still don't have access to any of my models and it's asking me to log in. We can get around this by just essentially deleting the sandbox and recreating it. And now it's going to pull in those environment variables. And the nice thing is is once you set your environment variable once, it'll have access to it forever. So, you don't have to worry about doing this normally. So, what we can do is we can say spxrm. That's going to remove the sandbox. and we give it the name of copilot test cuz that's what we called
- 7:54
this. And then it's going to essentially say, do you want to delete that? Yes, we want to delete that. And it's going to go through and it's going to delete everything related to that sandbox. This now means that we essentially need to recreate that sandbox. So we can just come in here, use that same run command to create a brand new sandbox. It's again going to take a little bit of time to start up your sandbox, but as you can see, it's relatively quick. And the nice thing is it actually has that credential injected inside of it. So now I can do
- 8:15
whatever I want. For example, I can say say hi. And it's going to actually prompt the AI and I get back the response of hi. And the nice thing is is if I try to access that GitHub off token, I can't. For example, if I just do echo dollar sign GitHub token, that is the GitHub token. And when I hit enter, you can see this is not the actual token. You can see spx proxy managed blah blah blah blah blah. That's
- 8:36
essentially saying that the container has no idea what the actual token is. The token is stored outside of this sandbox. And whenever a request is being made from the sandbox, what happens is the request is made. the request goes outside of the sandbox and then outside the sandbox this credential is injected in and replaced with the correct credential before it gets to GitHub. So
- 8:56
essentially there's a middleman that intercepts my request from the sandbox, adds that credential in and then lets it go across to the GitHub servers. This means it's impossible for any malicious script or anything that goes on in your code to access your credentials when you don't want them to. This is one of the big benefits of using this whether you're doing agentic coding or not. It keeps your credentials entirely safe on your system since there's no access available inside the sandbox. So, if I
- 9:18
downloaded a malicious package on this exact sandbox, it doesn't matter because it can't access my credentials or do anything to my system because it's entirely locked down. Speaking of, if I were to come in here and I just try to access various different files, for example, let's just try I cd into the root directory and then I want to run here ls. You'll notice that it only has
- 9:38
access to like a few different folders and files and all of this is inside of its own container. You can see the path here, g webbimplified current project. This is just essentially a copy of what is in my folder right here. So these files are being copied over. That's why you can see them here. But everything else on my system is entirely locked down and inaccessible from this workspace because it's all just these specific files and nothing else. To show
- 9:58
you what I'm talking about, we could really simply just say delete script.js. And this will actually go through. It'll delete that script.js JS file because it has access to that particular file. And if we were to come over to here, you can see that file has been removed just like we would expect it to. But if I have a file somewhere else on my computer, it is entirely impossible for it to access that. So let's just go ahead. We're going to exit out of here and I'm going
- 10:19
to create a brand new folder. We'll just call this test. And inside of here, I'm going to move that index html. And outside, I'm going to create a script.js. So you can see I have this test folder with index html inside of it and the script folder on the outside of it. Now, what I'm going to do is I'm going to CD into that test folder just like that. And I want to start my workspace here. So, I want to create a brand new sandbox. We're going to call
- 10:39
this C-pilot nested just like that. That's going to start up a brand new co-pilot sandbox inside this test folder, which means it only has access to the code inside this test folder and nothing else at all. So, if we just come in here and I were to run that ls command, you can see all we have is index.html that's in that test folder. And if I were to try to cd up a level
- 11:00
for example, and then I wanted to run my ls command, you'll notice it doesn't have access to anything else, you can see there's this agent MD file that's automatically added by Docker, essentially telling our agent that it's inside of a sandbox. But you can see this script file doesn't exist at all. And if I try to delete this, I can just say delete. And let's just copy the full path to this file and see if it'll delete it. We'll say copy path.
- 11:20
Paste that in. And when I hit enter, it's not going to be able to find or delete this at all because it doesn't have access to this file on my system anywhere. And no matter what it tries to do, you'll notice that this file is always going to be here no matter how many times I refresh. You can see it says it deleted it cuz it tried to run the command to delete it. But it doesn't have access to that file. So the file is still 100% here, not deleted at all. Now, this alone is already enough reason
- 11:41
to use sandboxes in my opinion cuz it gives me that peace of mind. I can run my AI without any safety checks and I can know that it won't mess up anything on my system at all. But obviously sometimes I want to take this a step further because right now it can still delete files inside this folder. It can add things into those files. It can inject code into like GitHub hooks and things like that. So it can still technically cause me problems by adding files into this folder. And then if I
- 12:03
run code, I could maybe run malicious code from these files. So there's one step of problem there. So if I wanted to be able to prevent that problem, what I can use is essentially something similar to GitHub work trees by cloning this entire thing instead of having it essentially linked to this. Because right now the way it works is every time I make a change inside of my sandbox, it duplicates that change outside my sandbox right here only for the files
- 12:24
that it has access to. This is great because it's super easy to work with, like I said. But again, you could run into potential security problems. If you have malicious code that adds like a GitHub pre-commit hook that does something malicious, that pre-commit hook will get added to your code and then if you try to commit something in GitHub, it'll run that. So if you run that code on your normal machine instead of in the sandbox, you could run into potential problems. So, let's go ahead
- 12:45
and look at the alternative we can use, which is going to be using a clone to clone this entire repository instead. Now, I'm just going to CD back to that main root level. Just like that. So, now we're at that root level we were at before. And what I want to do is I want to essentially run a brand new sandbox, but this one I'm going to be using that clone mode instead. So, I can just say d- clone just like this. That's going to allow me to run this as a clone. And
- 13:06
then I can specify that I want this to be copilot. And I can specify the name. We're just going to say copilot clone just like that. And we are immediately getting a warning saying that you must have a git repository to do this because it works off of git work trees. So we're just going to say get init to create a repository. There we go. And now we can run that clone command instead. And the big difference between the clone command and the
- 13:27
original command is that instead of just having essentially these files be modified directly between the two. What happens is it actually duplicates all these files into its own work tree that's entirely separate. And then if I were to make changes, for example, I can just say add a styles.css CSS file with nothing in it. There we go. So, it's just going to create a blank styles.css
- 13:48
file for me. And you'll notice it doesn't actually show up over here at all. You can see no matter how many times I refresh, this file does not show up over here. While if I actually check inside of here, you can see there's that styles.css file right there. Also, you notice none of the rest of these files are showing up. And that's because I need to make sure I add those files into my repository for them to show up. Because when you do a clone, it only clones whatever the current branch is
- 14:09
at. So, I need to have those committed into my branch. And since there was nothing committed to my branch, nothing is going to show up. So let's go ahead and actually fix that by just saying get add. And we'll just commit these files. There we go. So now we have those created. And to get this working properly, what I want to do is I want to remove that copilot clone sandbox that
- 14:30
we created. And this one, you'll notice we get a much longer warning message. The reason for that is because when you delete a cloned sandbox, all your files that are changed inside of there, unless you pull those down to your main repository, they will be deleted as all. So that's why it gives you a longer warning message. But now let's go ahead and we want to create this again. So let's just find that create command right here. That's going to clone everything. And now since we have these
- 14:50
committed to our actual repository, they will show up inside of our list. So if we just give that a second and I run that ls command, we should see all those different files just like we expect. And now I can say create a blank styles.css file. There we go. It's going to go through. It's going to create that file. And again, that file won't show up over here because it's only inside of
- 15:10
this specific git work tree. If I want to access that, I essentially need to pull that code down just like I was pulling down a pull request from GitHub. So, let's open up a brand new terminal. This terminal is inside my actual non- sandbox environment. And now what I can do is I can actually pull those different things down. So, I can say get checkout. And if I just do a quick search here, and actually let's do get fetch. And you'll see right here,
- 15:32
sandbox copilot clone. The name of this is always set to sandbox- whatever the name of the sandbox is. That's going to allow me to pull down the code that comes from that specific sandbox. Now that I've pulled down that sandbox code, I can come here with a get checkout. And if I look at my options, you'll notice I have the options for the code from that clone. So, let's just copy down that
- 15:53
clone just like that. And now that's pulling down the code from that section. Now, the problem is is that I didn't actually commit the changes over here. So, I should just say commit and add those changes. There we go. That way, it's going to make sure all those changes exist inside the repository. So, when I pull it down, they will be accessible. So, now it committed all those different changes. So, now if I were to just do a quick
- 16:13
fetch here to fetch all that down, and I were to check out that branch, you can see that blank styles.css is right there like I expect it to be. So, this essentially allows me to work in a way that I know that all of the code that is inside this section, if I'm pulling it down, I know that it could have potential problems inside of it. I should review it as if it was a pull request coming from somewhere else. Now, this again doesn't protect you if you
- 16:34
were to have malicious code inside of here and you run it. Obviously, that's your own problem that you ran into yourself. But this gives you that extra level of security cuz these files will never be modified unless you manually pull down the code. Now, the downside of this is it's much more cumbersome to work with. So, for the most part, I just work inside of noncloned environments because it's easier to work with. But if I had some more security concerns or
- 16:55
maybe things like pre-commit hooks and so on that were running often, I would maybe think about doing this in a slightly different way. Now, the next thing I want to talk about is going to be security policies that we can specifically use. So, let's go into our copilot. I just want to clear this out. We'll just restart this from scratch again. So, let's just get that running just like that. That's going to be that cloned version. And what I want to do is I want to show you how it doesn't have
- 17:16
access to like the web by default. It has access to some sites but not very many. For example, I can say, what is the latest ESLint version according to the docs? And I'm just going to paste in a URL to those docs. And when I run this, it's not going to be able to access these docs. It's going to get an error. As you can see, fetch failed. And that's because by default, it's locking down all of those different external API
- 17:38
routes. And it's not letting it access the internet except for on very specific pages where I told it it has that access. If we want to be able to modify this, for example, I want to be able to access that ESLint website, we can modify the policies that we have. So, if we say SBX policy and I type in network, that's going to give me all of my different network policies. And specifically, we want to list them. So, let's do policy ls and then we'll type
- 17:59
in network. And this is going to show all of the different things that are currently being allowed inside the network. So, you can see all of these are things being allowed by that kind of basic profile we set up that allows it to access lots of different things that you would normally need to access. for example like Debian and so on to download various different things. You can see like forcel and Google inside of here for common development related things Figma and so on. If we want to be
- 18:21
able to add to this, it's actually quite simple. I can just type in SBX policy allow. This is going to be a network policy and I'm going to make it global which means any sandbox has access to this. But if you want to restrict it to a specific sandbox, you could type in a specific sandbox name. And then we just need to put whatever that domain name is. Now in our case we want this to be eslint.org. So we can just paste in
- 18:43
eslint.org and that means any request to eslint.org is going to be acceptable. So now we can click enter on that. That rule has now been added as a local rule on my particular computer. And now if we were to come into here, let's just close out of this and restart it just to make sure that it gets that brand new network policy. And if we were to run that exact same command, we should hopefully see that it is able to correctly fetch this documentation. You can see it fetched
- 19:04
it. It was able to successfully fetch. There are no errors and it's given me the exact latest version. So that's how you can modify your different policies for what you can allow and deny access to. And if you wanted to, for example, deny access to something, you could just say deny in here, and that's going to explicitly add this as a denial rule. But oftent times, if let's say I want to remove this eslint.org that I added. All
- 19:26
we do is just do a list command. You can see right here, this is what I typed in as the thing that I allowed. All we need to do is just say spx, we want to do a policy. So we can say policy. RM is going to allow us to do a removal. This is global, so we'll pass in hash-g. And then this is the resource that we want to remove. So we'll say resource. And then whatever this is, eslint.org, we'll
- 19:46
just copy that over. And we'll hit enter. And we of course need to make sure that we specify that this is a network policy. That was a small mistake. There we go. And now it's going to remove that. You can see rule removed. And if we were to do a search for that, you can see it no longer shows up in our list. And again, if we were to try to make a request, it's going to be denied trying to make that request because it doesn't have access to that specific thing. It looks like it's just
- 20:06
regurgitating what I had before, but you get the idea. It would not be able to make that request because I removed that from my network policy. Now, the next thing I want to show you is how you can create your very own custom sandboxes. Cuz by default, if we look at the agent supported, you can see it's got pretty much all the big ones. But one that you'll may notice is not supplied is Pi. There's no PI support. But the nice thing is is they have this shell sandbox. And the shell sandbox is just
- 20:26
an empty shell. It's all it is. It's just an empty shell that you can log into. It's just like sshing into like a server, for example. That's what you have access to. And you can also customize what these empty shells will look like by creating different templates. So we look at this customize section. You can see there's this whole section on kit and that allows you to essentially create your very own custom sandboxes. And I'm going to show you how you can create one for PI that we can also use to work with local AI models.
- 20:48
So we can have a fully sandboxed local AI setup using PI, which is incredibly powerful. Now to get started with creating these custom sandboxes, all we need to do is we need to create a folder. We can call it whatever we want. We're just going to call this one PI. And inside that folder, we need to create a spec.yaml file. And this is where we define essentially everything for our agent. And by default, we can copy over from the documentation the
- 21:08
starting point for the cloud agent. I'm going to paste that in. Don't worry, I'll explain what everything does because a lot of this we don't even need for our PI setup. But this is what the cloud setup looks like directly from Docker. You can see that there's a Docker sandbox template for the Cloud Code Docker image. And then there's a bunch of other configuration that essentially allows Cloud Code to be installed and run directly inside of this. Now, in our case, since we're
- 21:28
using a custom shell, we don't want to use the cloud code docker image here. So, instead, we're going to use the shell Docker image. What that does is essentially allows us to use a completely blank sandbox with nothing in it, but it does have Docker installed, which means we can run Docker inside of our containers if we want to. So, that way we can use Docker in our sandbox. Now, the next thing we need to do is up here we have our schema version. Right
- 21:49
now, it's a version of one, so we'll leave it as one. We're creating a brand new agent, so we'll call this agent. And the name here is what we call from the command line. So when I was doing in my command line the different spx run copilot for example, this would now be replaced with pi because that's what I called the agent in this name section. Then inside of here is where we define where all of our different agent related code is. So our AI file name for this is
- 22:10
just agents.md. Claude's the only one that doesn't support this, but we're going to be using the agents.md. We'll leave persistence as persistence. So it kind of saves a volume with all of our memory. And then the entry point, this is just the command that gets run from the terminal. So in our case, all we need to do is just run pi. So we can put pi inside of here and this will run the pi command as soon as our thing starts up. So it'll essentially start up a
- 22:31
server and it'll run pi. That way we're immediately inside that pi terminal. Now there's an entire section on network and this is just saying what network connections are being allowed to be made. Now in our case for pi we only need to allow pi.dev. That's because whenever you start up pi it's going to reference out to pi.dev to see if there's a new version to be installed and if so prompt you to install it.
- 22:51
That's the only reason we're adding this in here. Otherwise, we don't need any other allowed domains. And this entire section here for service off and service domains. Since we're using PI as a local AI setup, we don't need any of this cuz all of that is for saving credentials and making sure your credentials are properly sandboxed and not accessible inside your image. But again, we're using this for local AI. So, we don't need that section. Same thing here with
- 23:11
credentials. We don't need this because we're not using any credentials because we're using entirely localbased AI. Now, if we wanted to, we could leave this is sandbox as one. Doesn't really matter. That's just a key that we can have in there so we know we're in a sandbox environment. But I'm going to be adding in the pi coding agent directory. I'm going to set that equal to the home directory right here. And then essentially it's going to allow me to do
- 23:32
is by default when you pass files into your actual new sandbox, they're going to be put in a specific home folder. And I want to make sure that pi uses that exact same home folder. That way it can sync up the files being created by our sandbox. This will be useful when we start talking about adding in that local setup as well. Finally, the last section is commands. And this is just what command runs when you install and set up
- 23:52
your sandbox for the first time. So we need to obviously install Pi on our computer to be able to use it. So what this command is going to do is we can just copy paste this entire thing with the npm install command for PI and that's going to go through and it's going to install PI. If we set the user to 1,00 that's going to be using the agent level user. So the user can either be set to zero or th00and. Zero is like
- 24:13
the root user and a th00and is a slightly more agent level user that has its permissions slightly revoked. So, I'd recommend using user 1000 in pretty much every use case you need. Next, we can just give this a name, install PI, and that's it. That's literally all it takes to set up our own PI agent. For the most part, all you need to do is set up your image as a blank shell docker. Then, you can specify your entry point, specify your install command to install
- 24:35
that, and then if you need to set up any environment variables or network related stuff, you can do that right here. That's it. Now, what we can do is we can run that. So, I can't just run sbx run pi. If I do that, it's going to say, hey, pi is not an actual sandbox. So instead, we need to reference a kit. So we can say d-kit and then pass it the directory to where this kit lives. Now it lives in this pi directory right here. So we're going to pass it that pi
- 24:56
directory. And now when I run this, you're going to notice it looks like we're actually getting an error. It says line 7, this field persistence is not found. So let's just go ahead and remove that. This could be that I'm running on a slightly older version of the Docker sandbox or things like that. This kit section is kind of experimental still. So things are changing rather rapidly in this space. But now if we actually run this, we should see it's creating a brand new sandbox. It's going to
- 25:17
download all the different things that it needs. It's also going to run that command which is right here, pi. And it's going to run the install command right here. And once it's done with all that, we should be booted up directly into Pi. And as you can see, we are now inside of a PI terminal. Right now, I don't have any models or anything set up, but I am in a PI terminal. So, we at least know that this is working just like we expected to, which is great. So,
- 25:37
now we can create any agent we want. And all we need to do is add in that local model support. Now, in my case, I'm going to show you how to set this up using LM Studio. As you can see here, I have LM Studio running and it's running on this exact local host port. So, if you want to learn more about how you can set up LM Studio from scratch to run local AI models, I have a full video. I'll link it in the cards and description for you. But for now, I'm going to assume you have LM Studio
- 25:58
running with various different models on it. The next thing that we need to do is we need to make sure that we can access local host. So, we need to expose a network that we have access to. In our case, we want to access that local host network, which in our case is 127.0.0.1 at port 1234. Now, I'm just going to change this to local host. I find it a little bit easier to read and work with. There we go. So, now we have our new
- 26:18
allowed domain of local host. The next thing that we need to do is we need to make sure that we have all the files for our model setup because the way that you set up local models in PI is you essentially create a JSON config file for those models and those live in the home directory right here that we just specified with this environment variable. So, let's create a brand new folder. We're going to call this folder files and this is built into the naming
- 26:39
of creating these kits. So if we create a folder called files and then inside that folder we create one called home. Essentially every single file we put in this home directory is going to be added to the home directory of our agent when we set it up and run it. So this is essentially just going to copy paste those files automatically for us. So let's create a brand new file in here called off.json. And we also need to create one called models.json.
- 27:02
Just like that. And for this off.json I'll just paste in the code. This is just what you need for LM Studio. You just need to give it a name. We'll call it LM Studio. The type is an API key and the key it doesn't matter what it is because you're using this locally. There is no real API key. So we can just paste in any value for key. It doesn't matter. This just tells PI we now have a new provider called LM Studio. And the way it connects is using this key called
- 27:22
key. But again there's no key. It's fully local. So it doesn't need to worry about that. The next more confusing part is going to be our models. I will again copy this over and explain what it is because it's mostly just going to be different configuration based on what model you're using. So here we have our providers and then on our providers we have LM Studio. This just must match whatever name you put in your off. From there, we then specify our base URL. And
- 27:42
you'll notice instead of putting local host here, what you normally would, I'm instead using this docker container here. So, I'm using host.docer.in. That just tells docker point towards local host. So, since we're running this in docker, instead of writing local host, we write host.docer.in internal. And that allows docker to properly send this to my computer instead of running
- 28:03
it inside the sandbox localhost. Next, we need to specify the API as open AI completions. Again, our API key doesn't matter. We can just put that as the same thing we have over in our off. And then finally, we can me specify what our models are. So, this ID just again comes from LM Studio. Whatever the name of the model you're using is, put that as your ID. Put whatever your context window is, whether or not it supports reasoning and
- 28:24
also what input modes it supports. And you can add as many or as few models as you want inside this section. And with that, that's literally everything we need to do to get this set up properly. Now, we have these files. they will be copied over automatically when we create a brand new sandbox. So what we need to do is first delete the sandbox that is currently running. So we can say xbx ls to see what our sandboxes are. You can see there's one called pi current
- 28:45
project. So let's just remove that one. Just like that. That's going to delete it. And then we can come in here and we can essentially create our sandbox. So let's just run that sandbox again. That's going to create a brand new sandbox using this kit. But since we added these off.json and model.json files, these are going to be automatically copied over for us. And you should see once we get this up and running that actually is running and we're connected to that Quen model. And
- 29:05
I can just say say hi. And it's going to take a while for the very first prompt to work just because I'm using a local model. It needs to warm up and load that in my GPU. But once it actually loads, it'll respond to us with that message. And from then on, it'll be much quicker. This is really just a problem with the startup speed of a local model. But again, this is just how local models work. And if you again want to learn more about how to set up your own local model, I'm going to link that video in the cards and description for you. It's
- 29:27
a very in-depth process, but it can save you a ton of money. And like I said, it's normally not this slow on each request. This is just the very first request, and I'm recording video at the same time. You can see follow-up requests are so much quicker. Now, another thing that's really nice about how these sandbox environments works is it doesn't copy over any of your global stuff. So, you'll notice inside of Pi, there's no skills or extensions or anything loaded because I don't have any of my global stuff. Everything is just
- 29:48
sandboxed inside this environment. And maybe you want to have some skills that you use in various different environments. Maybe you want to use the same skills in Pi and Copilot and Claude Code and so on. So, I'm going to show you a way that we can set that up as well. So, what we can do is we can create a brand new folder. I'm just going to call this one skills. And inside here, I want to create a spec.yamel. So, let's just copy the one we already have for Pi. And we'll paste
- 30:08
it into here. But, we're going to modify this drastically because instead of setting up an agent, we're going to be setting up a mixin. You can almost think of this like a plugin or an extension. Essentially, it's a bit of code that I can run every single time. And I can add it to any sandboxed environment that I want. It doesn't matter. And this allows me to add this to my PI terminal. It allows me to add it to cloud code, copilot, or anything else. We'll just
- 30:28
call this skills just like this because I'm going to use skills as an example. And since we're not using agents, we can remove that agent section. We can remove the network. We can remove the environment. Really, all we care about is a command that is going to install these skills for us. So, I'm just going to copy over the command for installing a skill right here. And I'll explain to you what we need this command to do. So, first we're calling npx skills add,
- 30:48
pasting it in the URL for our skill. And then to make sure this works properly, specify the skill you want. In our case, we're just using the default fine skills. We can make sure that it's a global install. And if we add dashy, that's going to make sure that it properly just runs through and sets everything up without us having to do user input. And then we can just call this install skills, whatever we want. This is going to just install one skill, but you can add as many or as few or
- 31:10
different extensions and so on inside here. And again, make sure your user is set to 1,00 to make sure that it's scoped down to the agent and not the root level. Now, we can add this directly into our sandbox if we wanted. So, let's come into here. Let's make sure that we clear this out so we have a fresh slate to work with and we have our running of our pi kit here and instead I want to add to this pi kit. So to do
- 31:31
that let's just say add and then we need to get the name of this project which I believe is just called current project pi. I think that is the name that it defaults to. And then we need to specify the location of our kit which is just that folder called I believe skills. There it is. And now if we click run on this that should hopefully try to add that. And it looks like I had a problem. And that's because I need to
- 31:52
make sure I call kit add so it knows to add this kit directly in. So now if we say kit add looks like my name is incorrect. I believe it is pi current project. There we go. Now that should go through and it's going to run all the commands and everything. You can see it's running that command right now. And now it has added that directly into my project. So what I can do is I can just start up that project that we have which
- 32:12
is pi current project. Just like that. And of course I need to make sure that I have the kit associated with it so it knows how to run pi properly. So I can say kit/py. There we go. Now that's going to run this. And you can see we have that scale installed. So this allows me to dynamically add content to these various different platforms however I want. So we can have a full agent-based kit right
- 32:33
here which is great if we want to set up essentially our very own PI or other agent. And we can add things like skills or if you want to install Python or various different packages. All that can be used in a mixin which you can add to any of these you want. And the nice thing is is you can actually specify these and save them in GitHub. So they don't need to be stored locally like this and you can just download them directly from GitHub. To show you an
- 32:53
example of what that looks like, I just actually have some sandbox examples here. I have pi, I have skills, and then I have the pi lm studio. And I want to show you how you can run these directly instead. So all you need to do is just take this URL that you get. We'll use the https URL cuz it's the easiest. We'll copy that URL to our clipboard. And then what we can do when we want to run our project, let's just again make sure we clear this. I want to run and I
- 33:14
want to set up a new one. So let's make sure we remove the current one. So we'll just say rm pi current project. That way we have a completely blank slate to start with. Give that a quick second to delete. And then we can run the run command to be able to start up a new one. So we can say run. I want to run pi. And instead of passing
- 33:35
in my folder of pi for my kit, I'm going to instead say kit, I'm going to take that entire URL. I'm going to paste it into here. And then for this URL, I want to make sure at the very start I put git with a plus afterwards. That way it knows it's accessing git. And since I have a folder called pi inside of here that I want to use, I can just make sure at the end I put the pound symbol. I type in diir for directory equal sign
- 33:56
and then whatever that folder name is, in our case pi. That's going to essentially reference this skill directly or not skill this kit directly from GitHub. And it's going to set up pi using that exact kit. And again, if we give it a second to run, we should see that we have pi set up directly inside of our terminal and it's coming from that GitHub repository. You can see I have pi. None of the models or anything are set up because this version on GitHub has the models separated, which
- 34:18
is great because now we have all this information that we want. And another really great thing is I can add multiple kits. Let's say that I wanted to add another kit onto this particular section. And this kit is going to be copied from that exact same URL. So, we'll just copy this URL here. Paste that down. Make sure I fix all the formatting that got messed up. There we go. And instead of having that PI directory, I instead want this to be the
- 34:39
PI LM Studio directory. There we go. And now this allows me to set this up using two kits at the same time. And actually, let's just make this one the skills kit. That way, we can test this with our skills properly. Now, this will fail because I already have that agent running. So, let's just do spx rm pi current project to delete the
- 34:59
one that currently exists. That way, we again get a fresh slate for testing this. And now, it's going to run both of those skills at the exact same time. And it looks like my removal maybe failed. Let's make sure that I actually remove this properly. There we go. And now we can run that and it'll set up. And again, it's going to go through and run both of these different kits. The first one is going to set up PI and the second one is going to add that skill in there. And we should see we have a PI setup
- 35:21
with no local models, but it should have that skill installed for us. Now, if we give it a quick second, you can see there's that skill and none of the local models. So, that's how we can set up all these different kits, store them on GitHub, and use them however we want. And these kits that I have created, the ones that we made in this video and so on, I'll link down in the description so you can access them and use them however you want. Now, there's a ton more that you can do with Docker Sandbox, and I
- 35:41
can't cover everything in this video, but this is 99% of what I use on my day-to-day basis with Docker Sandbox, and I honestly don't write any AI code unless it's inside of a sandbox from Docker Sandbox. I really want to give a huge thanks to them for sponsoring this video because I was pretty much going to make this video anyway, so them sponsoring is just a huge support for the channel. And if you want to check it out, it'll be linked in the description below. It's again entirely free for you
- 36:01
to use, and I highly recommend it because it makes your system so much more secure than if you didn't use it. And it will make you more productive as well. So you you don't have to babysit the AI and make sure it does everything properly. So with that said, please check out Docker Sandbox link down in the description below.
Description
SponsorTry for FREE Docker Sandboxes: https://utm.io/uq1EL
You most likely fall into one of two camps. You either check every single command the AI runs before it runs or you YOLO run AI with zero oversight. This either wastes your time or opens you up to huge security issues. That is why in this video I will show you how to sandbox your AI so you can save time and rest knowing your AI is unable to do anything malicious on its own.
📚 Materials/References:
Sandbox GitHub Kits: https://github.com/WebDevSimplified/d...
Local AI Setup Video: • The Best Local Agentic Coding Workflow (Co...
🌎 Find Me Here:
My Blog: https://blog.webdevsimplified.com
My Courses: https://courses.webdevsimplified.com
Patreon: / webdevsimplified
Twitter: / devsimplified
Discord: / discord
GitHub: https://github.com/WebDevSimplified
CodePen: https://codepen.io/WebDevSimplified
⏱️ Timestamps:
00:00 - Introduction
01:10 - Docker Sandbox Setup
02:37 - Sandbox vs Container
03:55 - Creating Your First Sandbox
06:02 - Securing Your Credentials
09:25 - Securing Your Files
11:49 - Adding Extra Security With Clones
17:00 - Setting Security Policies
20:13 - Creating Custom Sandbox Environments
22:42 - Adding Local Model Support
29:57 - Adding Skills To Your Agent
32:52 - Saving Templates In GitHub
#WebDevelopment #WDS #JavaScript
More from Web Dev Simplified
×2244:57The Best Local Agentic Coding Workflow (Complete Guide)
Web Dev Simplified1.1M views
×9.72:10Please… I Need Your Help
Web Dev Simplified459K views
×6.91:15:25The Ultimate MCP Crash Course - Build From Scratch
Web Dev Simplified327K views
×4.925:15I Will Never Use Prettier or ESLint Again
Web Dev Simplified231K views
