{"slug": "the-vms-powering-mobile-agents-instinct-claude-code", "title": "The VMs Powering Mobile Agents (Instinct, Claude Code)", "summary": "Anthropic's Claude Code mobile agent runs inside a Firecracker microVM with a custom Rust init (process_api) as PID 1, a 324 MB Bun harness on a read-only disk, and host-driven lifecycle management, according to an analysis of the ws-term project. The startup Instinct rents E2B sandboxes (Ubuntu 22.04.5, 2 vCPU, 1.9 GB RAM, 29 GB disk) to power its mobile agent, which emphasizes memory features.", "body_md": "One awesome product evolution is that agents (Claude Code, Instinct, Poke, etc) are moving off our local computers so that we can use them on our phones.\nUltimately this is great for the customer because that means the agent companies provide us with VMs for them to run on! Here's some notes on how the major platforms work based on looking around on [ws-term](https://github.com/RohanAdwankar/ws-term).\n\n## Claude Code on Your Phone\n\nClaude Code's box is its own **Firecracker microVM**, a KVM guest with its\nown kernel, booted straight into an init written in Rust:\n\n``` bash\n$ cat /proc/cmdline\n... rdinit=/process_api ... --listen-vsock-port 2024\n$ uname -r\n6.18.5-fc-v20                 # -fc- = Firecracker; a custom-built guest kernel\n$ ps -o comm -p 1\nprocess_api                   # PID 1 is not systemd; it's a Rust/Tokio binary\n```\n\n`process_api` is PID 1 and the host's control agent living\ninside your VM: it mounts the disks, then listens on **vsock port 2024** so the\nhost can drive the session from outside. That's the platform's defining trait:\n**the operator lives inside your tenant space**, and a lot of engineering goes into\nsealing it off (PID 1 is non-dumpable, `/proc/1/mem` is denied even with\n`CAP_SYS_PTRACE`, your shell is missing `CAP_SYS_RESOURCE`).\n\nThe disks split cleanly into *yours* (writable, persistent) and *theirs*\n(read-only, shared):\n\n``` bash\n$ lsblk -o NAME,SIZE,RO,MOUNTPOINT\nvda   256G  0  /                    # yours: writable, survives reclaim\nvdc   341M  1  /opt/claude-code     # theirs: the 324 MB `claude` harness (Bun)\nvdd  45.6M  1  /opt/env-runner      # theirs: the task launcher\nvde/vdf ... 1  /mnt/skills/...      # theirs: skills\n```\n\nThe **harness** is the thing running your tool calls and is a 324 MB compiled Bun\nbinary on a read-only disk. The model runs elsewhere; inference goes out as **Server-Sent Events over HTTPS/2** (not a WebSocket) to `/v1/messages`, through an egress gateway that is 443-only and\nMITM'd (`CN = Egress Gateway ... (production)`), with `api.anthropic.com` pinned\nin `/etc/hosts`. There is no inbound at all (` 192.0.2.2`, an RFC-5737 test\naddress). Auth is a **host-minted OAuth token**, cached root-only on disk and\nrotated per boot.\n\nLifecycle is host-driven and measured from the inside: **~430 ms** to init,\n**~6.4 s** to the harness process. Spin-up is triggered by an inbound message\n(the host wakes the VM over vsock and runs `--session-mode resume`); spin-down is\nidle reclaim decided by the host. When it's reclaimed, the *processes* die but\n`vda` detaches intact and reattaches on the next cold boot, which is why the\nconversation feels continuous even though the compute was destroyed.\n\n``` php\nflowchart TB\n  user([\"your keystrokes\"]) -->|http post| ingress[\"session-ingress\"]\n  ingress --> pa\n  hostctl([\"host control plane\"]) -->|vsock port 2024| pa\n  subgraph vm[\"Firecracker microVM\"]\n    pa[\"process_api, pid 1, Rust\"] --> harness[\"claude, 324 MB Bun harness\"]\n    vda[(\"vda (rw), yours, persists\")] --- harness\n    ro[(\"vdc/vdd/vde/vdf (ro), theirs\")] --- harness\n  end\n  harness -->|inference over SSE| gw[\"egress gateway, 443, mitm, api.anthropic.com\"]\n```\n\n## Instinct\n\nInstinct is a new startup which launched recently and it does some very nice things on the memory side which gives that feel of it being a real assistant rather than a chatbot.\n\n``` bash\n$ hostname\ne2b.local\n$ cat /.e2b\nn038afjvewg7jnc9pwdz\n```\n\n`e2b.local` means Instinct doesn't operate its own VM fleet; it rents\n[E2B](https://e2b.dev) sandboxes (\"sandbox-as-a-service\"), a throwaway Ubuntu box\nyou hand an agent so it has a computer:\n\n```\nUbuntu 22.04.5, 2 vCPU, 1.9 GB RAM, 29 GB disk, up ~30 min, user sandbox (uid 1001)\n```\n\nAnd let's look under the hood...\n\n``` bash\n$ systemd-detect-virt                  →  kvm\n$ cat /proc/cmdline\n  pci=off  virtio_mmio.device=4K@...  i8042.noaux i8042.nokbd  reboot=k  panic=1\n  clocksource=kvm-clock  root=/dev/vda  ip=169.254.0.21::...:eth0:off:tap0\n$ cat /sys/class/dmi/id/product_name   →  (empty)      # no SMBIOS at all\n$ ps -o comm -p 1                      →  systemd       # init=/sbin/init, not a custom PID 1\n```\n\nFirecracker again!\n\n`pci=off` + virtio-over-MMIO + empty DMI + `tap0` networking is the Firecracker\nsignature: no PCI bus, no SMBIOS, minimal devices. So both apps sit on the\n*same* microVM; the difference is who runs the fleet and **what boots inside it.**\nWhere Claude Code boots a stripped custom init (`process_api` as PID 1), E2B boots a\nfull Ubuntu with systemd and a whole XFCE desktop:\n\n``` bash\n$ systemd-analyze\nStartup finished in 265ms (kernel) + 992ms (userspace) = 1.258s\ngraphical.target reached after 977ms\n```\n\n~1.26 s to cold-boot all the way to a graphical desktop. The operator-in-guest exists\nhere too, but it's just E2B's `envd` running as an ordinary systemd service, not a\nsealed PID 1. E2B sandboxes are configurable too (you pick the vCPU, RAM, disk, and idle\ntimeout, and whether the box can be paused and resumed from a memory snapshot instead of\ncold-booted); Instinct runs a modest 2 vCPU / 1.9 GB desktop template.\n\nSo if the box is disposable, where does the agent's memory live? In a directory\ncalled `/memory`, and this is the platform's defining idea:\n\n``` bash\n$ cat /memory/README.md\nPersistent memory for [[rohan-adwankar]]\n$ ls /memory\nentities/  comms/  timeline/  workstreams/  knowledge/\n$ git -C /memory log --format='%an <%ae>' -1\nInstinct Agent <agent@instinct.com>\n```\n\nThe agent's memory is a **git repo of Markdown files** with `[[wiki-links]]`,\nnavigated by `grep`. The `timeline/` *coarsens* over time (raw → hourly → daily →\nweekly, like human memory), and the agent is literally the **git author**: it\ndoesn't call a memory API, it writes Markdown and commits it as itself.\n\nThe durable layer is that repo, pushed to **S3**, keyed by a per-user id, and\nstored not as files but as a single **git bundle**, which is a great little gotcha:\n\n``` bash\n$ git -C /memory remote -v\norigin  s3://instinct-prod-agent-memory/filesystem-memory/user-01M1VW7...\n$ aws s3 ls s3://.../user-01M1VW7.../ --recursive\n  HEAD\n  refs/heads/main/<sha>.bundle     # the ENTIRE vault, packed; `ls` after sync looks empty\n```\n\nAuth is **short-lived STS credentials**, not long-lived keys, so a leaked\nsandbox self-heals when the token lapses:\n\n``` bash\n$ cat /etc/instinct-aws-creds\nexport AWS_ACCESS_KEY_ID='ASIA…'     # ASIA prefix + session token = temporary STS\n...                                   # (values redacted, live secrets)\n# role: instinct-sandbox-observations-role\nflowchart TB\n  subgraph box[\"E2B sandbox (rented, disposable)\"]\n    agent[\"Instinct Agent (agent@instinct.com)\"] -->|writes and commits| mem[\"/memory, a Markdown vault git repo\"]\n    creds[\"/etc/instinct-aws-creds, short-lived STS\"]\n  end\n  mem -->|git push| store\n  creds -->|authorizes git push| store\n  subgraph store[\"S3 (durable, per-user)\"]\n    vault[(\"instinct-prod-agent-memory, the vault\")]\n    obs[(\"instinct-prod-observations, raw firehose\")]\n  end\n```\n\n*Durable thing = a git repo in S3. The machine is throwaway.*\n\nUsing a git repo for this is quite nice; the default structure seems to be like this:\n\n```\n  ~/instinct-vault/..         │󰫎  24 󰲡 Vault\n    .git                    │   23\n    comms                   │   22 Persistent memory for 󱗖 rohan-adwankar. Markdown + wiki-links, navigated by grep. Start here, then jump\n      chat                  │   21\n         rohan-adwankar--inst│   20 Who Rohan is, what he is working on, and what is connected live in entities/, workstreams/, and knowled\n    entities                │   19\n      people                │󰫎  18  󰲣 Layout\n         rohan-adwankar.md   │   17\n      projects              │   15 README.md\n         ws-term.md          │   14 timeline/     chronological record, coarsening upward: raw/ → hourly/ → daily/ → weekly/ → monthly/\n    knowledge               │   13 entities/     people/ projects/ — the nouns of Rohan's world, one file each\n      decisions             │   12 comms/        chat/ email/ meetings/ — one file per thread, named <who>--<topic>--<date>.md\n         x-account-signup-dec│   11 workstreams/  active/ completed/ someday/ — units of work; status: frontmatter matches the subdirectory\n      preferences           │   10 knowledge/    facts/ procedures/ preferences/ decisions/\n        instinct            │    8\n           autonomy.md       │    7 knowledge/preferences/instinct/ holds how Rohan wants the assistant itself to behave — autonomy, drafti\n           iteration-style.md│    6\n    timeline                │󰫎   5  󰲣 Conventions\n      daily                 │    4\n         2026-09-06.md       │    3 ● Every file has frontmatter with id, type, and aliases. [[id]] resolves to id.md or id/_index.md.\n    workstreams             │    2 ● Entity and knowledge files are updated in place and read as current state, never as a log. History li\n      active                │    1 ● Files that outgrow one page are promoted to a directory with an _index.md carrying the original id.\n         dragon-game-asset.md│  25  - Timeline and comms hold events and conversations; entities hold durable properties only.\n         startup-idea-search.│~\n    󰂺 README.md               │~\n~                             │~\n~                             │~\n\n:!tmux capture-pane -pS - | pbcopy\n```\n\nNow Claude Code's harness in the VM is open source and the same as normal but how about Instinct?\n\n``` bash\n$ ps -eo args | grep -E 'agent-exec|tools'\nagent-exec-server --port 8080                    # Go, runs the bash/code it's sent\ntools __internal_daemon --socket /tmp/.tools/bridge.sock \\\n      --base-url https://api.instinct.com/-/api/graphql/tool-execute   # Rust\n$ strings /usr/local/bin/tools /usr/local/bin/agent-exec-server \\\n    | grep -iE 'anthropic|openai|/v1/messages|x-api-key|claude|gpt|model'\n                                                 # → nothing. no model listed\n```\n\nSo it seems like there are no inference calls anywhere on the box. Claude Code seals the *operator*\ninside the guest; Instinct doesn't put the brain in the guest at all. The sandbox\nis a pure **execution surface**: `agent-exec-server` runs whatever bash the backend\nhands it, and every tool call (Gmail, the cloud browser, a payment) leaves as a\n**GraphQL request to `api.instinct.com`**, executed server-side. The `--base-url`\nis a runtime argument, not compiled in.\n\nFor the actual tool surface, rather than MCPs, Instinct seems to use a CLI for all tools:\n\n``` bash\nsandbox@e2b:~/ws-term-v1$ tools --help | wc\n     90    1466   10891\nsandbox@e2b:~/ws-term-v1$ tools --help\nTools CLI\n\nYou are a task agent. Your parent (the main agent) spawned you for a focused job. When the job is done, report back to your parent and hold. Your parent owns task-agent cleanup.\n\nYour direct-execute traits:\n  - work - integrations, files, web page fetching\n....\n\nRun `tools --help` for the current surface.\n\nUSAGE\n  tools <command-path> [options]\n  tools help [<path>...]\n\n....\n\nBuilt-ins (no help read needed): help, async wait, async list.\n\nEXECUTABLE (you can call these directly)\n  agent_message        namespace (1 action)   Send messages to other agents.\n  browser_guidance     namespace (3 actions)  Search per-config website guidance and this user's past outcomes before browser navigation, and record how a config performed after an attempt. Missing guidance is normal and means unknown, not supported or blocked.\n  cloud_browser        namespace (27 actions) Drive a cloud-hosted Chrome lease with the user's saved logins. Use for agent-driven web tasks (order food, book rides, compare prices, fetch receipts) that need real authenticated capability on real sites. A task agent acquires its own `lease_id` with `tools cloud_browser_scheduler acquire` and drives it here.\n\n....\n\nDELEGATED (only callable by the other role)\n  account              namespace (2 actions) Read the user's Instinct account profile.\n  feedback             namespace (3 actions) Submit product feedback and respond to team follow-ups.\n  generate_referral_link action                Ask the main agent to get the member's reusable referral link and current lifetime allowance.\n  revoke_referral_link action                Ask the main agent to revoke the member's reusable referral link.\n  speak                action                Generate a WAV speech file from a transcript, optional director's notes, and a voice.\n\nUNAVAILABLE (does not apply to this role)\n  steer_voice_agent    action     Send an answer or context into the user's active live voice session.\n```\n\nThe tool surface is the tell that this box isn't a coding sandbox at all.\n`tools --help` lists ~50 namespaces (Gmail, Notion, Slack, Stripe payments, a\ncredential vault), but the one that reveals the design is the **cloud browser**:\n\n``` bash\n$ tools --help | grep -iE 'cloud_browser|vault'\ncloud_browser            (27)  Drive a cloud-hosted Chrome lease with the user's saved logins.\ncloud_browser_scheduler  (4)   acquire, list, release, extend leases\nvault                    (7)   Manage, fill, and import the user's stored credentials\n```\n\nWhen Instinct orders food or books a flight \"as you,\" it does **not** open a\nbrowser on this sandbox. It *leases* one from a separate pool of cloud browsers,\neach carrying your saved profile (cookies and logins) and drives it through the\nsame `api.instinct.com` bridge:\n\n```\ntools cloud_browser_scheduler acquire      # → lease_id, on a browser \"config\" (a profile)\ntools cloud_browser <action>               # click / type / read / screenshot that Chrome\n```\n\nYou can read the whole model off how leases behave: each profile has exactly **one\nwrite lease** (the only session allowed to save new logins), up to five run at\nonce, and releasing a lease \"saves its cookies first, so the next lease loads\nthem.\" That persistence is the point: your browser identity is a **third durable\nthing**, sitting server-side next to the S3 vault and the observations index, kept\nso a disposable box can borrow it for one task and hand it back. Two details let\nit sign in without a secret ever touching the box:\n\n- **Scout before navigating.**`tools browser_guidance search` returns curated\n  per-site notes plus*your own* past outcomes per profile (`config-a: success` ,`config-b: blocked` ), a risk prior, not a verdict.\n- **Secrets go through the Vault, never chat.**`tools vault fill` types a stored\n  credential straight into the page; when the vault lacks one,```\ntools vault\n  request\n```\nmints a link*you* fill (`app.instinct.com/vault/fill?t=…` ). One-time\n  codes it reads itself from your connected Gmail or Outlook.\n\n``` php\nflowchart TB\n  ta[\"task agent, off-box\"] -->|acquire lease| sched[\"cloud_browser_scheduler\"]\n  sched --> cb\n  ta -->|click, type, read| cb[\"cloud browser with your saved profile\"]\n  vault[(\"Vault, your secrets, server-side\")] -->|fill| cb\n  cb -->|\"logged in as you\"| sites([\"Amazon, Uber, airlines, and more\"])\n  box[\"disposable E2B box\"] -->|\"issues tools calls, holds no cookies\"| cb\n```\n\n## Summary\n\n|  | **Claude Code** | **Instinct** | \n|---|---|---|\n| Isolation primitive | Firecracker microVM (KVM) | Firecracker microVM (KVM) | \n| Who runs the fleet | Anthropic, its own | E2B, rented, third party | \n| Guest inside the VM | Stripped custom init ( `process_api` ) | Full Ubuntu + systemd + XFCE desktop | \n| Cold boot (measured) | ~430 ms init, ~6.4 s to harness | ~1.26 s to graphical desktop | \n| What's durable | The machine ( `vda` block volume) | A git repo in S3 | \n| Memory model | Conversation state on disk | Markdown vault, git-versioned, agent-authored | \n| Credentials | Host-minted OAuth, on disk, rotated | Short-lived STS, role-scoped | \n| Backing store | Local virtio-block | S3, keyed by per-user id | \n| Harness location | On the box (324 MB Bun binary) | Off the box; the box holds two execution shims | \n| How the model is reached | SSE to `/v1/messages` via egress gateway | Never from the box; GraphQL to `api.instinct.com` , server-side | \n\nThis was pretty fun to take a peek, and I'll keep recording my notes as new products come around!", "url": "https://wpnews.pro/news/the-vms-powering-mobile-agents-instinct-claude-code", "canonical_source": "https://rohanadwankar.github.io/posts/platforms.html", "published_at": "2026-09-08 04:36:26+00:00", "updated_at": "2026-09-08 05:02:08.593333+00:00", "lang": "en", "topics": ["ai-agents", "ai-infrastructure", "ai-products"], "entities": ["Anthropic", "Claude Code", "Instinct", "E2B", "Firecracker", "ws-term", "Bun"], "alternates": {"html": "https://wpnews.pro/news/the-vms-powering-mobile-agents-instinct-claude-code", "markdown": "https://wpnews.pro/news/the-vms-powering-mobile-agents-instinct-claude-code.md", "text": "https://wpnews.pro/news/the-vms-powering-mobile-agents-instinct-claude-code.txt", "jsonld": "https://wpnews.pro/news/the-vms-powering-mobile-agents-instinct-claude-code.jsonld"}}