{"slug": "i-gave-my-coding-agent-its-own-computer", "title": "I gave my coding agent its own computer", "summary": "A developer detailed how they built a sandboxed virtual machine to give their coding agent its own computer, eliminating the risk of destructive commands. The setup uses Apple's Virtualization.framework on a Mac, with Linux guests, snapshots for rollback, and read-only file sharing. The developer shared lessons learned, including the framework's limitation to macOS and Linux on ARM, the importance of the EFI variable store, and the pitfalls of serial console attachment.", "body_md": "I run coding agents with shell access all day, and for a long time there was a low-grade dread underneath it. The permission prompts get annoying enough that everyone eventually turns them off, and then you are one confidently wrong `rm`\n\naway from a bad afternoon.\n\nWhat fixed it was not better prompting. It was giving the agent its own computer.\n\nThe setup is a VM on the same Mac. The agent gets a filesystem with nothing of mine in it, credentials that only work on throwaway repos, and a snapshot taken before every session. If it does something destructive I do not debug it. I roll back. Thirty seconds and the mistake never happened.\n\nThat part is easy to describe and, on Apple Silicon, surprisingly annoying to actually build. Here is what I learned doing it.\n\nThis is the first thing that trips people up, so it is worth being blunt about it.\n\n`Virtualization.framework`\n\nboots two things: **macOS on ARM** and **Linux on ARM**. That is the entire list. There is no Windows on ARM, and no amount of configuration will produce one. It is not a licensing gate you can argue your way past, there is simply nothing to enable. Anything you read that claims otherwise is describing UTM's QEMU backend or VMware Fusion, both of which use their own hypervisor rather than Apple's.\n\nFor the agent sandbox case this does not matter, because you want Linux anyway. Linux guests are smaller on disk, boot faster, and you are not burning one of your two permitted macOS guests on a machine that mostly runs `npm install`\n\n.\n\nThe two guest types are configured differently in ways the docs do not really foreground:\n\n```\nswitch os {\ncase .macOS:\n    // Boots the macOS bootloader, and needs a separate\n    // auxiliary storage file next to the disk image.\n    bootLoader = .macOS\n    platform = .mac(auxiliaryStoragePath: auxPath)\ncase .linux:\n    // Boots EFI, and needs a variable store that\n    // persists across reboots. Lose this file and the\n    // guest forgets where its bootloader is.\n    bootLoader = .linuxEFI(variableStorePath: efiVars)\n    platform = .generic\n}\n```\n\nThe EFI variable store is the one people lose. It is a small file that lives next to the disk image and holds the boot entries. Copy the disk image somewhere without it and you get a guest that boots to an EFI shell and looks broken.\n\nThis one cost me an evening, and I have never seen it written down.\n\nAttaching a serial console to a Linux guest is the obvious move when the guest fails silently. You get `hvc0`\n\npiped to a log file and you can finally see what the kernel is doing.\n\nExcept Ubuntu's installer detects the serial port and decides that is where you want to be. Subiquity moves the installation UI off the graphical display and onto the serial console, in a reduced text mode, and your VM window sits there showing what looks like a hang. The guest is fine. You just moved its face.\n\nSo the serial console has to be opt in, not always on:\n\n```\n// Only attach the console when actively debugging.\nconsoleLogPath: os == .linux\n    && UserDefaults.standard.bool(forKey: \"Serial\")\n    ? bundle.consoleLogURL.path : nil\n```\n\nThe general lesson generalizes past this one bug: on this framework, adding a device is never free. Every device you attach is visible to the guest and the guest may make decisions about it.\n\nMy first version of this mounted my real project directory into the VM through VirtioFS. It worked immediately and it was completely pointless, because an agent that can write to my actual project folder is just my main machine with extra steps.\n\nThe version that works: copy in, work, copy the diff out. The share is read only, or there is no share at all and everything moves over SSH.\n\nIf you do want a share, the tag matters and differs by guest:\n\n```\n// Linux guests: pick your own tag, then mount it:\n//   mount -t virtiofs myshare /mnt/share\nlet tag = \"myshare\"\n\n// macOS guests: use Apple's automount tag and the\n// share shows up at /Volumes/My Shared Files with\n// no mount command at all.\nlet tag = VZVirtioFileSystemDeviceConfiguration\n    .macOSGuestAutomountTag\n```\n\nThe macOS automount tag is genuinely nice and almost nobody knows it exists.\n\nIf your toolchain has an x86-64 binary in it somewhere, and it usually does, you can share Rosetta into an ARM Linux guest:\n\n```\nswitch VZLinuxRosettaDirectoryShare.availability {\ncase .installed:     // attach the share\ncase .notInstalled:  // offer installRosetta { ... }\ncase .notSupported:  // hide the feature entirely\n@unknown default:    // treat as unsupported\n}\n```\n\nMount it in the guest with `mount -t virtiofs rosetta /mnt/rosetta`\n\n, register it with `binfmt_misc`\n\n, and x86-64 binaries start running.\n\nTwo caveats before you build anything load bearing on this. Apple has signalled that Rosetta 2 is being wound down in future macOS releases, and has not committed to the Linux virtualization path surviving that. Check Apple's current guidance rather than mine. And handle `@unknown default`\n\nas unsupported rather than crashing, because the day this enum grows a case is the day your app stops launching.\n\n**Snapshot before, not after.** Obvious in hindsight. Cost me a session.\n\n**Real credentials get used.** If the agent can reach it, treat it as in scope. Scope the tokens to throwaway repos and assume anything reachable from that VM is reachable by the agent.\n\n**Disk images only grow.** The image expands toward whatever you provisioned and does not shrink when you free space inside the guest. Do not oversize \"just in case\". Keep one clean base image you never boot, clone off it, and delete the clone when you are done. One image that stays reasonable beats five that have all ballooned.\n\n**Two macOS guests per host, maximum.** That is Apple's license terms, not a technical limit. Nothing stops you, which means it is on you. Irrelevant for Linux guests, which is another reason to use them here.\n\nI run with permissions fully open now. The blast radius is a disk image I can throw away, so the prompt that used to make me think twice does not anymore.\n\nThe odd side effect is that I review the final diff more carefully than I used to. I am not spending attention on \"is this `rm`\n\nsafe\", so there is attention left over for whether the code is any good.\n\nAny of these will get you there. Apple's framework directly if you want to write the roughly two hundred lines yourself, and honestly it is a good weekend, the API is one of the better ones Apple ships. UTM if you want something free with a GUI and do not mind the setup. lume or Tart if you would rather script it.\n\nDisclosure: I got annoyed enough at the setup friction that I built a Mac app for this, called [Kyvenza](https://kyvenza.com). Every code sample above is from its engine. It is 49 dollars one time with a 7 day trial, and it will not run Windows either, for the reason in the second section. The approach is the point of the post though, and UTM does it for free if you do not mind the assembly.\n\nStill curious whether anyone has a cleaner pattern for handing credentials to a sandboxed agent. That is the part I like least.", "url": "https://wpnews.pro/news/i-gave-my-coding-agent-its-own-computer", "canonical_source": "https://dev.to/deland/i-gave-my-coding-agent-its-own-computer-4l99", "published_at": "2026-08-17 08:33:52+00:00", "updated_at": "2026-08-17 08:42:40.218594+00:00", "lang": "en", "topics": ["developer-tools", "ai-agents", "ai-infrastructure"], "entities": ["Apple", "Virtualization.framework", "Ubuntu", "QEMU", "VMware Fusion", "UTM"], "alternates": {"html": "https://wpnews.pro/news/i-gave-my-coding-agent-its-own-computer", "markdown": "https://wpnews.pro/news/i-gave-my-coding-agent-its-own-computer.md", "text": "https://wpnews.pro/news/i-gave-my-coding-agent-its-own-computer.txt", "jsonld": "https://wpnews.pro/news/i-gave-my-coding-agent-its-own-computer.jsonld"}}