<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Eugene Kovshilovsky</title>
    <description>The latest articles on DEV Community by Eugene Kovshilovsky (@ekovshilovsky).</description>
    <link>https://dev.to/ekovshilovsky</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3834512%2F4b023d5a-c8f2-45d7-80a8-a7efea3255b0.jpg</url>
      <title>DEV Community: Eugene Kovshilovsky</title>
      <link>https://dev.to/ekovshilovsky</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ekovshilovsky"/>
    <language>en</language>
    <item>
      <title>The OpenClaw Problem, or: How I Stopped Wrapping Docker in VMs and Built a One-Command Setup Instead</title>
      <dc:creator>Eugene Kovshilovsky</dc:creator>
      <pubDate>Fri, 27 Mar 2026 19:02:47 +0000</pubDate>
      <link>https://dev.to/ekovshilovsky/the-openclaw-problem-or-how-i-stopped-wrapping-docker-in-vms-and-built-a-one-command-setup-instead-1e7a</link>
      <guid>https://dev.to/ekovshilovsky/the-openclaw-problem-or-how-i-stopped-wrapping-docker-in-vms-and-built-a-one-command-setup-instead-1e7a</guid>
      <description>&lt;p&gt;&lt;em&gt;A continuation of &lt;a href="https://dev.to/ekovshilovsky/128gb-of-ram-zero-internet-and-a-year-of-building-ai-infrastructure-nobody-asked-for-4pc9"&gt;128GB of RAM, Zero Internet, and a Year of Building AI Infrastructure Nobody Asked For&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;When I shipped headless agent mode for Cloister, I thought I'd solved the hard problem. VM isolation, consent policies, credential forwarding — all the security layers you'd want between an autonomous AI agent and your actual life. The next step was obvious: run OpenClaw inside it.&lt;/p&gt;

&lt;p&gt;OpenClaw is an open-source AI agent framework — a persistent assistant that connects to Telegram, WhatsApp, iMessage, and runs tasks on your behalf. It has a gateway (the brain), nodes (execution endpoints), and a sandbox for running arbitrary code. It's powerful. It's also the kind of software whose community has been refreshingly honest about: &lt;strong&gt;don't run it on bare metal&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;So I didn't. I ran it inside a Cloister VM. Inside Docker. Inside a Linux VM. On macOS.&lt;/p&gt;

&lt;p&gt;If that sentence made you wince, you're ahead of where I was.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three Security Bypasses and a Caddy Proxy
&lt;/h2&gt;

&lt;p&gt;The first sign something was architecturally wrong came when I tried to access OpenClaw's Control UI through an SSH tunnel. Three things broke, each requiring a security bypass:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;CSP Header.&lt;/strong&gt; OpenClaw's CSP hashes didn't match its actual inline scripts. The UI rendered as a blank page. Fix: a Caddy reverse proxy to strip and replace the header. Not ideal.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Device Auth.&lt;/strong&gt; Docker NAT meant the gateway saw &lt;code&gt;172.18.x.x&lt;/code&gt; instead of &lt;code&gt;127.0.0.1&lt;/code&gt;, triggering device pairing over HTTPS — which SSH tunnels don't provide. Fix: &lt;code&gt;dangerouslyDisableDeviceAuth: true&lt;/code&gt;. The flag's name judged me every time I opened the config.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Gateway Bind.&lt;/strong&gt; Loopback binding + Docker port publishing don't mix. Fix: &lt;code&gt;gateway.bind: lan&lt;/code&gt;, which is exactly what the security docs tell you not to do.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I rationalized all three: "The VM has three isolation layers. Even with bypasses, the blast radius is contained." Technically true. Also a Rube Goldberg machine where each layer existed to compensate for the layer below it.&lt;/p&gt;

&lt;p&gt;Then I found OpenClaw's Lume documentation.&lt;/p&gt;

&lt;h2&gt;
  
  
  The "Wait, They Already Solved This" Moment
&lt;/h2&gt;

&lt;p&gt;OpenClaw has a docs page titled "macOS VM." I'd skimmed past it. When I actually read it:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/trycua/lume" rel="noopener noreferrer"&gt;Lume&lt;/a&gt; runs macOS VMs on Apple's Virtualization.framework — same hypervisor tech Cloister uses through Colima for Linux, but with macOS guests. The key differences that made me rethink everything:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No Docker.&lt;/strong&gt; OpenClaw installs natively. It has its own sandbox — the one I was redundantly wrapping in another Docker layer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No CSP bypass.&lt;/strong&gt; Gateway runs on actual localhost. No NAT, no proxy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No auth bypass.&lt;/strong&gt; Connections come from real localhost. Device pairing just works.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No bind hack.&lt;/strong&gt; Gateway stays on loopback. Everything works as documented.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;iMessage access.&lt;/strong&gt; Lume VMs are real macOS — they can run BlueBubbles for iMessage bridging. Linux VMs can't. Ever.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Five problems solved by removing a layer of abstraction. I chose the option that meant admitting the layer I'd built was wrong: abstract the VM backend so Cloister supports both. Colima for Linux workloads, Lume for macOS. The user says &lt;code&gt;--openclaw&lt;/code&gt;, Cloister picks Lume. The user creates a regular profile, Cloister picks Colima. Nobody needs to know which hypervisor is underneath.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Installation Gauntlet
&lt;/h2&gt;

&lt;p&gt;If you've tried setting up a Lume macOS VM yourself, you've probably already rage-quit once. Here's what actually happens:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The IPSW download.&lt;/strong&gt; Lume creates a macOS VM from Apple's ~18GB restore image. It downloads to a temp directory. If the setup fails — and it will — the download is gone. Next attempt? Another 35 minutes on a decent connection. I built IPSW caching into Cloister so it downloads once, keeps it at &lt;code&gt;~/.cloister/cache/ipsw/&lt;/code&gt;, and reuses it across every attempt and every profile. The kind of feature nobody thanks you for until they don't have it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The version mismatch.&lt;/strong&gt; Apple's Virtualization.framework refuses to install a macOS guest newer than the host. It downloads the 18GB IPSW, starts the install, and rejects it &lt;em&gt;after 25 minutes&lt;/em&gt;. I added a preflight check that parses the IPSW filename version, compares against &lt;code&gt;sw_vers&lt;/code&gt;, and fails in five seconds instead of twenty-five minutes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Setup Assistant.&lt;/strong&gt; Lume automates the macOS Setup Assistant by clicking through 167 UI elements via VNC. On macOS 26.4, Apple renamed a button. Step 66 of 167 fails. Every time. This is an upstream Lume issue — if you're doing it yourself, you'll need to create the VM without &lt;code&gt;--unattended&lt;/code&gt; and click through setup manually via VNC. Five minutes by hand. Not ideal, but you only do it once per base image.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The base image.&lt;/strong&gt; Cloister solves this with a shared base image that every OpenClaw profile clones from. First profile: 15-20 minutes. Every subsequent one: about 2 minutes. Plus snapshots — factory (after provisioning) and user (your fully configured state) — so &lt;code&gt;cloister reset&lt;/code&gt; gets you back to working in under two minutes.&lt;/p&gt;

&lt;p&gt;If you're setting this up without Cloister, budget an afternoon and a decent internet connection. Or save yourself the trouble.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Real Problem: Nobody Knows Where to Start
&lt;/h2&gt;

&lt;p&gt;The Lume setup gets you a blank macOS VM. Congratulations. Now you need to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Configure passwordless sudo&lt;/li&gt;
&lt;li&gt;Install Xcode Command Line Tools, Homebrew, Node.js, Playwright, OpenClaw&lt;/li&gt;
&lt;li&gt;Run the OpenClaw daemon onboard with the right flags&lt;/li&gt;
&lt;li&gt;Set up a Telegram bot via BotFather&lt;/li&gt;
&lt;li&gt;Figure out your Telegram user ID (hint: message &lt;code&gt;@userinfobot&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Configure Ollama as a provider — but first figure out the VM's bridge gateway IP&lt;/li&gt;
&lt;li&gt;Set up Google OAuth for Gmail, Calendar, Drive — but the OAuth callback goes to localhost, and you're inside a VM, so you need an SSH tunnel&lt;/li&gt;
&lt;li&gt;Deal with the macOS keychain password that drifted from the user password during headless setup&lt;/li&gt;
&lt;li&gt;Register the node host with OpenClaw's device pairing system&lt;/li&gt;
&lt;li&gt;Approve the device — but not with &lt;code&gt;--all&lt;/code&gt; because that flag doesn't exist, it's &lt;code&gt;--latest&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Each of these has at least one gotcha that'll cost you 30 minutes of Googling — or 30 seconds of asking ChatGPT, which will confidently hallucinate the wrong config field name and cost you an hour. Some have gotchas that'll cost you a day.&lt;/p&gt;

&lt;p&gt;I know because I hit every single one of them.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;cloister setup openclaw&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;So I built a wizard.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cloister create &lt;span class="nt"&gt;--openclaw&lt;/span&gt; my-oc    &lt;span class="c"&gt;# Create the Lume VM (clones from base image)&lt;/span&gt;
cloister setup openclaw my-oc       &lt;span class="c"&gt;# One command. Handles everything.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The wizard runs five sections in order. Each one writes config and credentials immediately — never batches at the end. If your WiFi drops halfway through, re-running the wizard picks up from the last completed step.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Credentials.&lt;/strong&gt; Detects 1Password CLI. If found, stores everything there with Touch ID. If not, asks if you want to install it or use local encrypted storage. Generates a new keychain password (because the headless setup almost certainly left it in a state where the old one doesn't work), stores it &lt;em&gt;before&lt;/em&gt; applying it to the VM (credential sync invariant — never change a password you haven't recorded), and stores the VM user credentials.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Channels.&lt;/strong&gt; Walks you through BotFather for Telegram — or accepts an existing bot token. Collects your user ID with instructions to message &lt;code&gt;@userinfobot&lt;/code&gt;. Writes the config with &lt;code&gt;dmPolicy: allowlist&lt;/code&gt; and &lt;code&gt;allowFrom&lt;/code&gt; locked to your Telegram ID. I initially used &lt;code&gt;allowedUserIds&lt;/code&gt; because that's what seemed logical. OpenClaw rejected it. The actual field is &lt;code&gt;allowFrom&lt;/code&gt;. The kind of thing where you stare at a "Config invalid" error for twenty minutes before realizing the docs and the schema disagree.&lt;/p&gt;

&lt;p&gt;WhatsApp gets configured as an action-only channel — OpenClaw can send you notifications, but nobody can issue commands through it. Safe defaults: &lt;code&gt;allowCommands: false&lt;/code&gt;, &lt;code&gt;allowFrom&lt;/code&gt; locked to your number, &lt;code&gt;escalationPolicy: deny&lt;/code&gt;. Because the last thing you want is someone in a WhatsApp group accidentally telling your AI agent to &lt;code&gt;rm -rf /&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Providers.&lt;/strong&gt; Auto-detects Ollama on the VM's bridge gateway IP. Lists available models. Offers to pull &lt;code&gt;qwen3:32b&lt;/code&gt; if nothing's loaded. Then asks the real question: local inference (free, fan sounds like a jet engine) or Anthropic Claude (costs money, quiet). Both can be registered simultaneously — you just pick a default. The wizard handles the API key collection and 1Password storage.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Google OAuth.&lt;/strong&gt; This one was fun. &lt;code&gt;gog&lt;/code&gt; (the Google CLI) needs to run inside the VM, but the OAuth callback redirects to &lt;code&gt;localhost&lt;/code&gt; — which is the VM's localhost, not your Mac's. The wizard picks a random high port (49152–60999, collision-checked), sets up an SSH tunnel, runs &lt;code&gt;gog auth add&lt;/code&gt; with &lt;code&gt;--listen-addr 0.0.0.0:&amp;lt;port&amp;gt;&lt;/code&gt;, and the callback routes through the tunnel back to gog. You just paste the URL in your browser and click Allow.&lt;/p&gt;

&lt;p&gt;Except gog's file-backed keyring needs a password to encrypt tokens, and there's no TTY available for the prompt. The wizard sets &lt;code&gt;GOG_KEYRING_PASSWORD&lt;/code&gt; to the keychain password it already stored. Another 45 minutes of debugging, now a single environment variable in the setup flow.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Device Pairing.&lt;/strong&gt; Registers the node host, writes trusted proxy config, approves pending devices, and runs &lt;code&gt;openclaw gateway probe&lt;/code&gt; to verify the whole thing is actually healthy — WebSocket connectivity, RPC health, auth warnings. Not just "is the process running" but "can we actually talk to it."&lt;/p&gt;

&lt;p&gt;The whole flow takes about 3 minutes on a running VM. Non-interactive mode works too:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cloister setup openclaw my-oc &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--telegram-token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"BOT_TOKEN"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--telegram-user-id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"USER_ID"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--default-provider&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ollama &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--ollama-model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;qwen3:32b &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--google-client-secret&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;~/client_secret.json &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--google-email&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"you@gmail.com"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;And for AI agents that want to discover what's configurable:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cloister setup openclaw my-oc &lt;span class="nt"&gt;--list-options&lt;/span&gt; &lt;span class="nt"&gt;--json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  The Config Serialization Bug That Almost Ate Everything
&lt;/h2&gt;

&lt;p&gt;While building the wizard, I discovered that older versions of Cloister (the one installed via Homebrew) would silently drop the &lt;code&gt;backend: lume&lt;/code&gt; field when saving config. Go's &lt;code&gt;yaml.v3&lt;/code&gt; library ignores struct fields it doesn't recognize during unmarshal, so the old binary would load the config, not understand &lt;code&gt;backend&lt;/code&gt;, and write it back without it. Next time the new binary loaded the config, it would default to Colima and everything would break.&lt;/p&gt;

&lt;p&gt;The fix was three things: remove &lt;code&gt;omitempty&lt;/code&gt; from the Backend field (always serialize it), add a config version number (so we can detect and migrate), and rotate &lt;code&gt;config.yaml&lt;/code&gt; to &lt;code&gt;config.yaml.prev&lt;/code&gt; before every save. If anything goes wrong, your previous config is one file rename away. Simple, boring, correct.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Agent Command That Had to Die
&lt;/h2&gt;

&lt;p&gt;The original Cloister had a whole &lt;code&gt;agent&lt;/code&gt; subcommand tree — &lt;code&gt;agent start&lt;/code&gt;, &lt;code&gt;agent stop&lt;/code&gt;, &lt;code&gt;agent status&lt;/code&gt;, &lt;code&gt;agent logs&lt;/code&gt;, &lt;code&gt;agent forward&lt;/code&gt;. This made sense when OpenClaw ran inside Docker inside a Linux VM. The VM was one thing, the Docker container was another, and you needed separate controls for each.&lt;/p&gt;

&lt;p&gt;With Lume, the VM &lt;em&gt;is&lt;/em&gt; the agent. OpenClaw runs as a launchd service, not a Docker container. There's no separate lifecycle to manage. So I replaced the entire agent subcommand tree with unified profile commands: &lt;code&gt;cloister status&lt;/code&gt; shows everything (both backends, VM state, gateway health), &lt;code&gt;cloister logs&lt;/code&gt; tails the gateway log, &lt;code&gt;cloister stop&lt;/code&gt; stops the VM. One interface, regardless of what's running underneath.&lt;/p&gt;

&lt;p&gt;The old &lt;code&gt;cloister agent&lt;/code&gt; command now prints a polite deprecation message pointing to the new commands. Because breaking people's muscle memory without a breadcrumb trail is rude.&lt;/p&gt;
&lt;h2&gt;
  
  
  What I Learned (Again)
&lt;/h2&gt;

&lt;p&gt;When I hit the Docker networking problems, my instinct was to add complexity. A proxy for the CSP. A flag to disable auth. A bind override. Each solution was individually defensible. Together, they formed a tower of compensating controls that existed only because the foundational decision — running a macOS-native application inside Docker inside Linux inside macOS — was wrong.&lt;/p&gt;

&lt;p&gt;The Lume approach doesn't need any of those fixes because it doesn't create any of those problems.&lt;/p&gt;

&lt;p&gt;Removing abstraction layers is harder than adding them. You have to admit the layer you built was wrong. You have to resist the voice that says "but it works fine with the three workarounds." It does work fine. But when you're building infrastructure that autonomous AI agents run on, "fine" is measured in security bypasses you hope nobody exploits.&lt;/p&gt;

&lt;p&gt;The setup wizard is the part I'm most satisfied with. Not because the code is clever — it's mostly prompts, SSH commands, and state tracking — but because it encodes every gotcha, every undocumented field name, every TTY password issue, every keychain drift problem into a flow that takes 3 minutes instead of a day. The next person who wants to run OpenClaw in an isolated macOS VM doesn't need to learn any of this. They just run one command.&lt;/p&gt;

&lt;p&gt;That's what infrastructure is supposed to do: absorb complexity so the user doesn't have to.&lt;/p&gt;




&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/ekovshilovsky" rel="noopener noreferrer"&gt;
        ekovshilovsky
      &lt;/a&gt; / &lt;a href="https://github.com/ekovshilovsky/cloister" rel="noopener noreferrer"&gt;
        cloister
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Run multiple Claude Code accounts &amp;amp; AI agents on one Mac — isolated macOS VMs with secure sandboxing
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Cloister: Isolated VMs for AI Agents &amp;amp; Multi-Account Claude Code on macOS&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;&lt;a href="https://github.com/ekovshilovsky/cloister/actions/workflows/ci.yml" rel="noopener noreferrer"&gt;&lt;img src="https://github.com/ekovshilovsky/cloister/actions/workflows/ci.yml/badge.svg" alt="CI"&gt;&lt;/a&gt;
&lt;a href="https://github.com/ekovshilovsky/cloister/releases/latest" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/4528e8f94a033aeb1865f2c455801842905fea88a5f74872da88cd79a24777d8/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f762f72656c656173652f656b6f767368696c6f76736b792f636c6f6973746572" alt="Release"&gt;&lt;/a&gt;
&lt;a href="https://goreportcard.com/report/github.com/ekovshilovsky/cloister" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/787c249f1171f7ac0d189afc4e9309a31335dc88ee9ef42da705ddc9f239bf15/68747470733a2f2f676f7265706f7274636172642e636f6d2f62616467652f6769746875622e636f6d2f656b6f767368696c6f76736b792f636c6f6973746572" alt="Go Report Card"&gt;&lt;/a&gt;
&lt;a href="https://github.com/ekovshilovsky/cloister/LICENSE" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/08cef40a9105b6526ca22088bc514fbfdbc9aac1ddbf8d4e6c750e3a88a44dca/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d4d49542d626c75652e737667" alt="License: MIT"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Isolated macOS VM environments for running multiple Claude Code organizations, sandboxing autonomous AI agents like &lt;a href="https://openclaw.ai/" rel="nofollow noopener noreferrer"&gt;OpenClaw&lt;/a&gt;, and separating credentials across client engagements.&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/ekovshilovsky/cloister/demo.gif"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fekovshilovsky%2Fcloister%2Fdemo.gif" alt="cloister demo"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Why cloister?&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Multi-account isolation.&lt;/strong&gt; Claude Code stores credentials, conversation history, and project config in &lt;code&gt;~/.claude&lt;/code&gt;. If you work across multiple organizations, every session shares the same identity. cloister gives each account its own isolated VM with separate credentials, CLAUDE.md, and conversation history.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Autonomous agent containment.&lt;/strong&gt; AI agents like OpenClaw run 24/7 with shell access, browser control, and cron scheduling. cloister's VM isolation is stronger than Docker (separate kernel, not just namespace isolation) — services inside the VM are unreachable from the host unless explicitly tunneled, and &lt;code&gt;cloister stop&lt;/code&gt; is an instant kill switch.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Dual-backend architecture.&lt;/strong&gt; Colima (Linux VMs) for Claude Code isolation and Docker workloads. Lume (macOS VMs via Apple Virtualization Framework) for OpenClaw…&lt;/p&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/ekovshilovsky/cloister" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;&lt;em&gt;&lt;a href="https://github.com/ekovshilovsky/cloister" rel="noopener noreferrer"&gt;cloister&lt;/a&gt; — Isolated VM environments for AI coding agents and multi-account separation. Dual-backend: Colima (Linux) and Lume (macOS). One-command OpenClaw setup.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href="https://github.com/ekovshilovsky/op-forward" rel="noopener noreferrer"&gt;op-forward&lt;/a&gt; — Forward 1Password CLI across SSH boundaries with biometric authentication.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>macos</category>
      <category>devops</category>
      <category>opensource</category>
    </item>
    <item>
      <title>128GB of RAM, Zero Internet, and a Year of Building AI Infrastructure Nobody Asked For</title>
      <dc:creator>Eugene Kovshilovsky</dc:creator>
      <pubDate>Fri, 20 Mar 2026 02:28:54 +0000</pubDate>
      <link>https://dev.to/ekovshilovsky/128gb-of-ram-zero-internet-and-a-year-of-building-ai-infrastructure-nobody-asked-for-4pc9</link>
      <guid>https://dev.to/ekovshilovsky/128gb-of-ram-zero-internet-and-a-year-of-building-ai-infrastructure-nobody-asked-for-4pc9</guid>
      <description>&lt;p&gt;Most people who know me professionally won't see my name on LinkedIn attached to a company right now. That's intentional. It's not that I'm between roles, it's that the moment you attach a title and a company name to yourself on this platform, your inbox becomes a graveyard of BizDev pitches, QA outsourcing firms promising to "augment your team," and vendors who want to "explore synergies" over a 30-minute call that could have been a "no." I've done that dance for years. I'd rather write code.&lt;/p&gt;

&lt;p&gt;What I will say: I'm still a CTO. I'm still leading technical strategy. The scope has changed, and I'm working with a smaller, sharper group of people on things I actually care about, but the role hasn't. I just happen to be writing a lot more code than I have in a decade, and that is the point of this article.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Shift
&lt;/h2&gt;

&lt;p&gt;About a year ago, I was running e-commerce engineering at CarID. Large teams, enterprise-scale platform, the usual orbit of architecture reviews and vendor evaluations. Over the years I'd pushed hard for modern data tooling through Delta architectures, real-time pipelines, and I'd wave-tested every AI tool that came across my desk. GitHub Copilot, early LLM integrations, various coding assistants. My honest assessment each time was the same: interesting, but I wouldn't let it near a production PR.&lt;/p&gt;

&lt;p&gt;Then, around February and March of 2025, something shifted. Not gradually! It felt like someone flipped a switch while I wasn't looking. Models got reliable enough to trust with real work. Claude Code shipped and it wasn't a toy or a glorified autocomplete, it was a genuine development partner that could hold context, reason about architecture, and do actual engineering. The ecosystem around it started moving fast, and for the first time in years, I felt like I was watching something I needed to be inside of, not evaluating from a distance.&lt;/p&gt;

&lt;p&gt;By mid-April 2025, I left CarID. In my free time, I moved into building AI-powered developer infrastructure, the kind of tooling that makes the difference between AI being a novelty and AI being a force multiplier. I'm still leading and setting technical direction, making architecture decisions, building systems but I'm also deeper in the work itself than I've been since the early days of my career.&lt;/p&gt;

&lt;p&gt;And I am very willing to get my hands dirty.&lt;/p&gt;

&lt;p&gt;What started as "let me properly integrate AI into my workflow" turned into a year-long obsession with building with AI reliably, securely, and without depending on things I can't control. Along the way, I recently decided to open-source the pieces because I realized the problems I was solving weren't just mine.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem Nobody Warns You About
&lt;/h2&gt;

&lt;p&gt;Here's the thing about using AI seriously across multiple contexts: the tools assume you're one person with one account doing one thing.&lt;/p&gt;

&lt;p&gt;That's adorable.&lt;/p&gt;

&lt;p&gt;I have work projects with confidentiality requirements. I have personal projects I'm developing on my own time with my wife and kids. I would never use company resources for personal development, and I'd never mix personal context with professional context. That's not how I operate, the Gemini in me, and if you've ever worked in enterprise, you know that's not how anyone should operate.&lt;/p&gt;

&lt;p&gt;So I ended up with multiple Claude Code licenses, multiple LLMs, multiple agents running simultaneously and a very practical problem: how do you keep all of this isolated on one machine without everything bleeding into everything else?&lt;/p&gt;

&lt;p&gt;I found out the hard way that Claude Code's config isolation is broken. Instructions from one context started showing up in sessions for another. Not obviously but subtly, in ways that only became visible over time. The digital equivalent of accidentally presenting the wrong client's slides, except it happens silently. That's the kind of thing that erodes trust in your tools, and trust is the whole game when you're delegating real work to AI.&lt;/p&gt;

&lt;h2&gt;
  
  
  So I Did What Any Reasonable Person Would Do...I Built a VM Orchestrator!
&lt;/h2&gt;

&lt;p&gt;When config-level isolation doesn't work, you need process-level isolation. Separate filesystems. Separate kernels. Real boundaries. Normal people might just log out and log back in. I chose violence.&lt;/p&gt;

&lt;p&gt;I built &lt;a href="https://github.com/ekovshilovsky/cloister" rel="noopener noreferrer"&gt;cloister&lt;/a&gt;, an open-source CLI that creates lightweight Linux VMs using Apple's Virtualization Framework, where each profile gets its own isolated environment while sharing your code workspace, SSH keys, and Claude Code plugins. Type "cloister work" and your terminal background changes color, tunnels auto-discover host services, and you're in an isolated shell with Claude Code installed. Everything works, nothing leaks.&lt;/p&gt;

&lt;p&gt;But getting here wasn't a straight line. Not even close.&lt;/p&gt;

&lt;h2&gt;
  
  
  The War Stories
&lt;/h2&gt;

&lt;p&gt;My M5 Max has 128GB of RAM. Absolute unit. When I tried to run Ollama with a 27-billion-parameter model on it, it crashed immediately. After a day of debugging Metal shader compiler output, I found the issue: six lines of code mixing bfloat and half types in a matrix multiplication kernel. The fix had been merged upstream three months earlier, but Ollama hadn't synced it yet.&lt;/p&gt;

&lt;p&gt;Six lines.&lt;/p&gt;

&lt;p&gt;Between a working 27B model and me on the most powerful laptop Apple makes. Sometimes the universe has a sense of humor. I cloned Ollama from source, applied the fix, and wrote up a &lt;a href="https://github.com/ekovshilovsky/cloister/blob/main/docs/guides/ollama-m5-metal4-fix.md" rel="noopener noreferrer"&gt;full diagnostic guide&lt;/a&gt; so other M5 owners don't have to go through the same detective work.&lt;/p&gt;

&lt;p&gt;Then I learned that Apple does not expose Metal GPU access to Linux VMs through any hypervisor API. None. Zero. Apple looked at that feature request and said "no" in every API they ship. The workaround, a Vulkan translation through three layers, is the GPU equivalent of translating English to French to Japanese to get your point across at a dinner party.&lt;/p&gt;

&lt;p&gt;So I tunneled around it. Ollama runs on the host with native Metal acceleration, and an SSH reverse tunnel forwards it into each VM. Full GPU speed, zero translation overhead, and the VM is just a thin client sending prompts and receiving tokens.&lt;/p&gt;

&lt;p&gt;Along the way I also discovered that NVM silently breaks bash strict mode, GPG commit signing is a three-layer lock-contention nightmare in VMs, Claude Code switched installers mid-project, and a single Claude session can consume 37GB of virtual memory. I lost a 20-minute coding session to an OOM kill before I learned to allocate proper swap.&lt;/p&gt;

&lt;h2&gt;
  
  
  Credentials Shouldn't Live in Plaintext
&lt;/h2&gt;

&lt;p&gt;One of the things that kept me up at night about running AI agents in VMs was credential management. The standard approach is to export API keys as environment variables in plaintext, sitting in memory, accessible to any process that asks nicely.&lt;/p&gt;

&lt;p&gt;Inside a VM running autonomous agents with a published history of 512 security vulnerabilities? That's not a security concern. That's a security &lt;em&gt;invitation&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;I built &lt;a href="https://github.com/ekovshilovsky/op-forward" rel="noopener noreferrer"&gt;op-forward&lt;/a&gt;, a daemon-tunnel-shim that forwards 1Password CLI commands from the VM to the Mac host, where Touch ID handles the authentication. Every credential access triggers my actual fingerprint. If the VM is compromised, the attacker gets an op binary that responds to every request with the digital equivalent of "go ask your mother."&lt;/p&gt;

&lt;p&gt;I also built a consent system for the VMs themselves. Interactive profiles get full access to host services such as clipboard, 1Password, Ollama. Headless agent profiles get nothing unless explicitly whitelisted. SSH keys, GPG keys, Downloads are locked out by default. It's like giving your house keys to a stranger because they promised to only use the kitchen. Except I don't give them the keys.&lt;/p&gt;

&lt;h2&gt;
  
  
  Working at 35,000 Feet
&lt;/h2&gt;

&lt;p&gt;This is the part that matters most to me.&lt;/p&gt;

&lt;p&gt;Picture this: I'm on a flight. The guy next to me is watching downloaded Netflix episodes. The WiFi costs $8 and delivers bandwidth that would have embarrassed a 56k modem.&lt;/p&gt;

&lt;p&gt;I'm running a 27-billion-parameter language model doing code review on my laptop. Locally. On a GPU that Apple won't let my VMs touch directly, so I tunneled around the restriction.&lt;/p&gt;

&lt;p&gt;When the model needs a database password, it asks 1Password through a daemon-tunnel-shim chain that triggers Touch ID. The VM's Claude Code session has its own credentials and history and is completely isolated from every other context on my machine.&lt;/p&gt;

&lt;p&gt;No internet. No API calls leaving the aircraft. No tokens counted against a rate limit.&lt;/p&gt;

&lt;p&gt;I can disconnect my Mac from the internet and keep working. Fully. The entire AI development stack runs on one machine with zero external dependencies.&lt;/p&gt;

&lt;p&gt;That's not an accident. That's what I designed for, because I got tired of my productivity being at the mercy of someone else's infrastructure.&lt;/p&gt;

&lt;h2&gt;
  
  
  What This Year Taught Me
&lt;/h2&gt;

&lt;p&gt;The biggest shift wasn't technical. It was in how I think about leverage.&lt;/p&gt;

&lt;p&gt;For most of my career, leverage meant hiring. More people, more output. Simple math, complicated HR.&lt;/p&gt;

&lt;p&gt;Now, leverage means infrastructure. The right local setup lets one person do what used to require a team. Not because AI replaces people...please, let's retire that talking point, but because it changes the bottleneck. The bottleneck used to be typing speed and debugging time. Now it's context management, credential security, and compute orchestration. Nobody taught us this in management training.&lt;/p&gt;

&lt;p&gt;The CTO title hasn't changed, but what the job feels like has. I spend less time in meetings about product design and architecture and more time building directly. I spend less time reviewing pull requests and more time pair-programming with an AI that doesn't care if I refactor the same function four times.&lt;/p&gt;

&lt;p&gt;The tools I've built this year aren't products I'm trying to sell. They're infrastructure I needed to work the way I want to work. I open-sourced them because I think more people are going to need this same infrastructure, and I'd rather they spend their time building things instead of solving the same plumbing problems I already solved.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;If you're a senior technical person who's starting to work with AI seriously and not just asking it to explain regex, but actually integrating it into your workflow, then you're going to hit these walls. The identity isolation wall. The GPU acceleration wall. The credential security wall. The "what happens when the internet goes down" wall.&lt;/p&gt;

&lt;p&gt;I've hit all of them, usually face-first. Here's what's on the other side.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;&lt;a href="https://github.com/ekovshilovsky/cloister" rel="noopener noreferrer"&gt;cloister&lt;/a&gt; — Isolated VM environments for AI coding agents and multi-account separation.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href="https://github.com/ekovshilovsky/op-forward" rel="noopener noreferrer"&gt;op-forward&lt;/a&gt; — Forward 1Password CLI across SSH boundaries with biometric authentication.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href="https://github.com/ekovshilovsky/cloister/blob/main/docs/guides/ollama-m5-metal4-fix.md" rel="noopener noreferrer"&gt;M5 Metal 4 fix guide&lt;/a&gt; — Step-by-step diagnostic and fix for Ollama on Apple M5 chips.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>devops</category>
      <category>macos</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
