The Flag Beats the Hardware: Investigating Claude Cowork, Linux VMs, and Hypervisor Gates in the Age of AI

🤖 Read Raw Markdown📄 Google Doc (Try: Tools/Audio/Listen to document summary)

Setting the Stage: Context for the Curious Book Reader

Context for the Curious Book Reader

As artificial intelligence tools evolve to touch local files and run remote tasks, developers face an intricate web of architectural choices. In this interesting chapter of our ongoing exploration, we examine how Anthropic’s Claude Cowork handles local file binding versus cloud sandboxing. Through practical terminal diagnostics and log forensics on a NixOS workstation with hardware virtualization disabled, this treatise uncovers how platform flags can bypass hardware checks entirely—offering valuable lessons for maintaining a clean, minimal-surface local development environment in the Age of AI.


TL;DR: Claude Cowork is Anthropic’s agent mode across the Claude desktop app, web app, mobile app, and Chrome side panel. As of September 2026 its sessions run by default in an isolated sandbox on Anthropic’s servers, while the desktop app still ships a second, local execution mode whose shell sandbox is a virtual machine gated on a hardware hypervisor: shell commands and any code Claude writes execute inside a dedicated Linux VM, isolated from the host operating system by the platform’s hypervisor (Apple Virtualization.framework on macOS, Hyper-V on Windows), with QEMU/KVM filling that role on Linux. This article traces, with command receipts from a NixOS workstation where KVM is deliberately disabled, what that split means in practice: local files reach cloud sessions through a per-call bridge in the desktop app rather than any bulk upload; the Linux build in use (1.24012.0, wrapped by a pinned community flake) refuses its VM by a platform flag before any hardware check; the device-registration and folder-grant machinery for cloud sessions is present regardless; and the “Project or folder” selector under the prompt is machine-local working context, distinct from the account-level Projects list. Anthropic’s Linux apt build had reached 1.40609.0; the pin was left in place on purpose.


Technical Journal Entry Begins

MikeLev.in: How can the browser-based Claude in the Chrome browser have a “Cowork” button if that works on the local system using sandboxes and such? Is that even possible in a web browser?

Fable 5 Extra: I need to check the current docs on this, since these products change fast. Let me look.Your mental model was accurate for the original Cowork, but the architecture changed: the sandbox moved to Anthropic’s servers, so the browser is now just a client.

When using Cowork in the cloud, Claude’s work runs on Anthropic’s servers instead of your computer, and your sessions and files are saved to your Claude account. That’s what makes a Cowork session possible in a Chrome side panel (or on claude.ai, or on your phone) — the isolated environment, file creation, skills, connectors, and scheduled tasks all execute remotely. Work continues in the background. Close your laptop and Claude keeps going. Per the help center, sessions run in the cloud on every surface, including desktop.

The Chrome extension adds one thing the cloud sandbox can’t do on its own: see and act on the tab you’re looking at. The side panel starts a Cowork session, so there’s no need to select “Cowork” first. That’s why it appears as a full session rather than a separate chat.

What still needs your actual machine is anything that touches local resources, and for that the desktop app has to be open even though the session itself runs in the cloud: local file access, local connectors, browser use, and computer use from web and mobile work through the Claude Desktop app. One nuance for the Chrome case specifically: in the Chrome side panel, Claude can read the tab you’re on without the desktop app. Claude driving a browser as part of a task still needs the desktop app open.

For the isolation details, the help center points to a separate architecture overview: https://support.claude.com/en/articles/14479288

Cloud Execution Versus Local Reality

MikeLev.in: So if you have a folder in your home directory on your local system and you set it as a “Project” so that folder is selectable from the dropdown menu on the machine it was set up on but if you go to another machine running Claude Desktop that same project is not over there. Are you saying that EVERYTHING in that folder gets sucked up into the cloud? Credentials and all? Does it have to be a git repo? Is it turned into a git repo silently in the background or is it using some other mechanism like rsync? What you’re suggesting is a wholesale slurping down of potentially huge folders so that local-like behavior requiring credentials and such happens on Anthropic’s server? I have the Linux version of Claude Desktop Claude 1.24012.0 (44be96) 2026-07-21T16:43:51.000Z and for the Mac it’s Claude 1.40609.0 (f65e38) 2026-08-27T16:19:51.000Z. On the Mac version I have one of these types of projects set up and on the dropdown menu for projects on the Linux version with the same exact user login it says “No matches”. This could be the Linux version being behind or something being wrong about your explanation.

Fable 5 Extra: Good questions, and the answer is “no, it doesn’t slurp the folder” — but let me pull the architecture doc so I’m not guessing about the mechanism.No wholesale slurp. The mechanism is a brokered, on-demand bridge, not a sync — and your Linux “No matches” is expected behavior, not a bug.

Probing the Linux VM Gate

How local files actually reach a cloud session. When a session in the cloud needs something on the user’s device, like a local file or the browser, the request goes through the Claude Desktop app on that device over an Anthropic-brokered connection. Local file access is limited to folders the member has connected on the desktop, and each local tool call is checked against the member’s permissions before it runs. So it’s per-call RPC: the sandbox asks the desktop app “read this path,” the app checks the folder is one you connected, and returns the bytes. If the desktop app is offline, a session in the cloud can’t reach the device. No git, no rsync, no background clone. A folder only “exists” to the cloud session while your Mac is awake with the app open, and only the specific files Claude touches ever leave it.

But your credential worry is still valid, just narrower. Because a session in the cloud runs on Anthropic’s servers, the agent’s work, including any local files it opens through the desktop app, is processed on Anthropic’s servers rather than staying on the device. So if Claude decides to cat .env inside a connected folder, that content goes up. The “short-lived credentials only” language in the docs refers to Anthropic’s own tokens — connector authorization tokens never enter the sandbox — not to your files. The practical mitigation is the one you’d guess: connect narrow folders, keep secrets outside them.

Why the Linux box says “No matches.” Cowork projects live locally on your desktop and are built around the tasks you run through Cowork. Under current limitations: projects are desktop-only and stored locally. There’s no cloud sync for project data at this time. The project definition (name, folder binding, instructions, memory) is metadata on the Mac’s disk. Same account on another machine sees nothing because there’s nothing account-side to see. That’s the design, not a version lag.

One caveat on the Linux build itself: the Cowork projects article lists only Claude Desktop for macOS and Windows as supported, and third-party guides say no official Linux build exists for Claude Cowork, so a Linux app could be a community repackage that lags Anthropic’s releases. Your July-vs-August version gap fits that. But even a current official build wouldn’t show the Mac’s project, for the reason above.

One last thing worth knowing: the cloud model is the default, but local execution remains available for existing desktop deployments, where the agent loop and code execution run on the member’s device. If your Mac install predates the cloud rollout, you may still be in that mode, in which case file contents only go to Anthropic as part of model prompts, not into a server-side sandbox.

MikeLev.in: Okay, here’s how my system is set up doing it. I deliberately have the Linux KVM feature turned off that would need to be activated for Cowork. This is also contrary to your suggestion of where the code execution works. Why are all the virtualization technologies across Mac, Windows and Linux need to be turned on (vet that / do they?) in order for Cowork to work if it’s cloud-based? That sounds like what you do for a local sandbox.

Fable 5 Extra: Context verified. The KVM question is a sharp one, and it deserves a docs check rather than a guess — let me vet the requirement.Context verified. Your instinct is correct, and the docs back it: the hypervisor requirement is the local sandbox, and the desktop app never dropped it when cloud sessions arrived. Both things are true at once, and I overstated the cloud side last turn.

What the VM is for, per Anthropic’s own docs. The architecture overview describes two execution modes, and in the local one shell commands and any code Claude writes execute inside a dedicated Linux VM, isolated from the host operating system by the platform’s hypervisor (Apple Virtualization.framework on macOS, Hyper-V on Windows). Linux is the same pattern with different parts: on Linux, Cowork runs those tasks in a virtual machine that the desktop app hosts with QEMU and KVM, needing VT-x in firmware, the qemu/ovmf/virtiofsd packages, and user access to both /dev/kvm and /dev/vhost-vsock; without VT-x the tab reports “Cowork requires hardware virtualization (KVM)”. On Windows the enterprise install doc says Cowork also requires working hardware virtualization, which the readiness check verifies. So yes: all three platforms gate on a hypervisor, and it is exactly what you said it sounds like.

Why a cloud-default product still demands it. Three reasons, two documented and one observed:

  1. Local execution didn’t go away. Local execution remains available for existing desktop deployments: the agent loop and code execution run on the member’s device. A Linux-beta bug report describes a per-task chooser, “Run this task → On your computer”, whose Bash sandbox is the local VM. Cloud is the default session mode, not the only one, so the app still ships the VM.
  2. The gate is at app launch, not per session. The app checks these requirements once at launch and disables the Cowork tab otherwise. No “I only want cloud sessions” bypass is documented.
  3. Observed coupling: that same bug report says when the local VM failed to start, cloud-mode Cowork sessions should link to this computer (no “Couldn’t link this session to a computer” banner) but didn’t — the device bridge for connected folders went down with the VM. That was filed as a bug against build 1.18286.2, older than your 1.24012.0, so read it as “at least one Linux build coupled them,” not as design.

So the precise statement is: the cloud sandbox needs none of your hypervisor, but the desktop app that hosts the Cowork tab won’t open the tab without one. “Sessions run in the cloud on every surface” was the help center’s framing and I repeated it too cleanly.

Two corrections to my last turn. First, there is an official Linux build — Anthropic ships a .deb through its own apt repo, in beta, for Ubuntu 22.04 or later, or Debian 12 or later. Your own packages.nix comment already said so (aaddrick repackages the official .deb); I should have weighted that over the third-party “no Linux app” guides. Second, “no cloud sync for project data” still stands, but on your box there’s a simpler reason the dropdown is empty: with KVM off, the Cowork tab is gated at launch, so there is no Cowork for projects to live in.

What this means for the NixOS box. Reading the config as compiled: mike has no kvm in extraGroups, boot.kernelModules is empty (no vhost_vsock), and an FHS wrapper doesn’t inherit apt’s recommended qemu/ovmf/virtiofsd. So even with VT-x flipped on in firmware, three more named gates would still fail. Your posture is coherent: keep the hypervisor off, and the KVM-free Cowork path from that machine is the Chrome side panel or claude.ai, where the sandbox is Anthropic’s and nothing local is reachable. The cost, per the coupling in point 3, is that the Linux box likely can’t serve as a device bridge for cloud sessions either — no connected-folder access from it, which is probably what you want anyway.

Uncovering the Platform Flag


1. PROBES (read-only; each is a BEFORE reading)

grep -c -E '(vmx|svm)' /proc/cpuinfo
lsmod | grep -E '^(kvm|vhost)' || echo "no kvm/vhost modules loaded"
ls -l /dev/kvm /dev/vhost-vsock 2>&1
id -nG | tr ' ' '\n' | grep -c '^kvm$'
readlink -f "$(command -v claude-desktop)"
tail -n 20 ~/.config/Claude/logs/cowork_vm_node.log 2>&1

Line 1 proves whether the CPU exposes VT-x/SVM at all (0 means firmware-off or unsupported; nonzero means the kernel can see it). Line 2 falsifies “KVM is off” at the kernel layer — udev autoloads kvm_intel on most boxes regardless of boot.kernelModules, so a loaded module here means the firmware gate is open and only the group/vsock gates hold. Line 3 shows whether the device nodes exist and who owns them. Line 4 confirms the missing kvm group membership the config implies. Line 5 confirms which store path the FHS wrapper resolves to, tying the 1.24012.0 version string to the pinned flake rev. Line 6 is the log the Linux-beta bug report used; its last lines name which gate the app hit at launch, and a missing file is itself a receipt that this build logs elsewhere.

2. NEXT CONTEXT (adhoc.txt lines, copy-symmetric with the probes)

! grep -c -E '(vmx|svm)' /proc/cpuinfo
! lsmod | grep -E '^(kvm|vhost)' || echo "no kvm/vhost modules loaded"
! ls -l /dev/kvm /dev/vhost-vsock 2>&1
! id -nG | tr ' ' '\n' | grep -c '^kvm$'
! readlink -f "$(command -v claude-desktop)"
! tail -n 20 ~/.config/Claude/logs/cowork_vm_node.log 2>&1
/home/mike/repos/nixos/packages.nix
/home/mike/repos/nixos/configuration.nix
/home/mike/repos/nixos/hardware-configuration.nix

3. PATCHES

No repo patches required. The question was architectural, and the config as compiled already expresses the intended state (hypervisor off). No ignition required.

4. PROMPT (caboose copy for prompt.md)

The KVM/vsock/group receipts and the cowork_vm_node.log tail from the NixOS box are in context. Given that this machine will never enable a local hypervisor: (a) confirm from the log which named gate the Linux Claude Desktop build hits at launch, (b) state whether this box can still act as a device bridge for cloud Cowork sessions or whether the Chrome side panel / claude.ai is the only Cowork path from it, and (c) if the aaddrick FHS flake rev pinned in packages.nix is now behind the official apt .deb, say what the delta is without proposing an upgrade patch yet.

5. EXTERNAL DELIVERABLES

None.

MikeLev.in: Okay, I don’t want to turn on KVM on my Linux machine, having it off is part of my whole simplicity and less surface-area first approach. I’m not turning KVM on my Linux box until I really, really need it and this doesn’t qualify.

1: Probe (before):

$ git status
On branch main
Your branch is up to date with 'origin/main'.

nothing to commit, working tree clean
(nix) pipulate $ grep -c -E '(vmx|svm)' /proc/cpuinfo
lsmod | grep -E '^(kvm|vhost)' || echo "no kvm/vhost modules loaded"
ls -l /dev/kvm /dev/vhost-vsock 2>&1
id -nG | tr ' ' '\n' | grep -c '^kvm$'
readlink -f "$(command -v claude-desktop)"
tail -n 20 ~/.config/Claude/logs/cowork_vm_node.log 2>&1
0
no kvm/vhost modules loaded
ls: cannot access '/dev/kvm': No such file or directory
crw-rw-rw- 1 root kvm 10, 241 Jul 30 09:41 /dev/vhost-vsock
0
/nix/store/i7x4cc8v80864k3y6zck53lbpkn2sjl8-claude-desktop-bwrap
2026-07-23 22:20:05 [info] [linux-vm-helper] [cowork-linux-helper] 2026/07/23 22:20:05.589889 [server] listening on /run/user/1000/claude-cowork-vm.sock
2026-07-29 10:13:10 [info] [VM] Loading vmClient (TypeScript) module...
2026-07-29 10:13:10 [info] [VM] Module loaded successfully
2026-07-29 10:13:11 [info] [linux-vm] Spawning helper: /nix/store/8ir6v1idn0fb4w31bkp8pfr5aph63ghz-claude-desktop-1.24012.0/lib/claude-desktop/resources/cowork-linux-helper
2026-07-29 10:13:11 [info] [linux-vm-helper] [cowork-linux-helper] 2026/07/29 10:13:11.055549 starting (pid=29927, socket=/run/user/1000/claude-cowork-vm.sock)
[cowork-linux-helper] 2026/07/29 10:13:11.057156 [server] listening on /run/user/1000/claude-cowork-vm.sock
2026-07-29 10:13:11 [info] [cleanupVMBundleIfUnsupported] yukonSilver not supported (status=unsupported), checking for stale bundle...
2026-07-30 12:33:23 [info] [VM] Loading vmClient (TypeScript) module...
2026-07-30 12:33:23 [info] [VM] Module loaded successfully
2026-07-30 12:33:23 [info] [cleanupVMBundleIfUnsupported] yukonSilver not supported (status=unsupported), checking for stale bundle...
2026-07-30 12:33:27 [info] [linux-vm] Spawning helper: /nix/store/8ir6v1idn0fb4w31bkp8pfr5aph63ghz-claude-desktop-1.24012.0/lib/claude-desktop/resources/cowork-linux-helper
2026-07-30 12:33:27 [info] [linux-vm-helper] [cowork-linux-helper] 2026/07/30 12:33:27.299645 starting (pid=18157, socket=/run/user/1000/claude-cowork-vm.sock)
2026-07-30 12:33:27 [info] [linux-vm-helper] [cowork-linux-helper] 2026/07/30 12:33:27.301216 [server] listening on /run/user/1000/claude-cowork-vm.sock
2026-08-26 10:29:23 [info] [cleanupVMBundleIfUnsupported] yukonSilver not supported (status=unsupported), checking for stale bundle...
2026-08-26 10:29:23 [info] [VM] Loading vmClient (TypeScript) module...
2026-08-26 10:29:23 [info] [VM] Module loaded successfully
2026-08-26 10:29:25 [info] [Bundle:status] rootfs.img missing
2026-08-26 10:29:25 [info] [Bundle:status] rootfs.img missing
2026-08-26 11:06:53 [info] [Bundle:status] rootfs.img missing
2026-08-26 11:06:53 [warn] [startVM] VM not supported (linux/x64), skipping
(nix) pipulate $ 

2: Context (after):

# adhoc.txt    _   _   _ to set context____ _   _  ___  ____  _   Simpson Couch Gag Here (explain anything to the audience you feel needs it explained)
#     / \   __| | | | | | ___   ___   / ___| | | |/ _ \|  _ \| |  
# ahe/ _ \ / _` | | |_| |/ _ \ / __| | |   | |_| | | | | |_) | |  Claude Desktop a local VM or not? Both at once? Cowork is weird.
# ahc ___ \ (_| | |  _  | (_) | (__  | |___|  _  | |_| |  __/|_|  
#  /_/   \_\__,_| |_| |_|\___/ \___|  \____|_| |_|\___/|_|   (_)  
# Ad Hoc CHOP: The Not-Managed-by-Git Safe-for-Client-Data place  

# OPTIONAL BUT BIG FOR FULL CONTEXT-WINDOW STORYTELLING
# ! python scripts/articles/lsa.py -t 1 --reverse --fmt dated-slugs  # <-- The "Rolling Pin" that gives the 40K foot book-spine view of book-ore.
# GLOSSARY.md                 # <-- I think this glossary goes well with the book-ore spine to do world building.
# scripts/articles/lsa.py     # <-- Useful for refining commands like `posts`, critical to Second Brain concept.
# ~/repos/nixos/autognome.py  # <-- Letting the AIs really understand my environment (The Brave Little Tailor punches above Their Weight Class proving the dunning-kruger effect the gate-keeper's (lower-case) lament.)
# init.lua                    # <-- Daily driver hot-keys that overlap with aliases in flake.nix
 
# STILL BIG BUT LESS OPTIONAL (especially flake.nix)
# flake.nix                   # <-- THE ONE BIG THING TO INCLUDE Infrastructure as Code (IaC) tells LLM about your system down to the metal
# prompt_foo.py               # <-- This very content-compiling system
# foo_files.py                # <-- This is the router, evolving book outline and the things you pin-up to produced the recursive self-improvement loops

# TINY ILLUMINATING (OK to include every time / automatically = `apply.py`, `.gitignore`, `.gitattributes`)
# requirements.in             # <-- All known dependencies and (necessary) version pinning. WORA gotcha's exposed.
# __init__.py                 # <-- Master versioning
# pyproject.toml              # <-- The PyPI Packaging details

# OPTIONAL ACTUATORS (cheap and good to include to expand the AI's capabilities)
# cli.py                      # <-- Catch-all actuator for PyPI envs, Python anchoring, MCP tool-call (plus alternatives) and **kwargs like wrapping for CLI
# scripts/xp.py               # <-- Transforms host OS copy-paste buffer player-piano music into context-payload.
# scripts/ai.py               # <-- How I constantly use local AI to write git commit messages with `m` alias.
# scripts/crawl.py            # <-- Feel free to ask for something to be crawled and included in the next turn.
# scripts/weblogin.py         # <-- Lets the user "warm up" the cache for their web logins at their leisure on a profile that persists.
 
# MISCELLANEOUS (rare to include but sometimes critical)
# scripts/foo_cartridge.py    # Needs description
# scripts/foo_replay.py       # Needs description
# release.py                  # <-- How everything ends up where it does (GitHub, PyPI, etc.)
# imports/voice_synthesis.py  # <-- The wand can talk to you
# imports/ascii_displays.py   # <-- Where all the ASCII Art lives
# scripts/release/version_sync.py  # <-- Needs to be wrapped into release.py and eliminated, I think.

#                         --- Under this line is were you paste what the AI gives you ---
#                         --- We call it context but it's really just the right-hand  ---
#                         --- blast-radius of the "probes" to make this all science.  ---

# --- END `adhoc.txt` TEMPLATE ---

# STICKBUG & MOTHER CAT KATA
# assets/trails/botify_pageworkers.yaml
# assets/installer/mck.sh
# assets/installer/replay.sh
# assets/trails/first_context.yaml
# assets/trails/practice.yaml
# assets/trails/public_walk.yaml
# scripts/bookmark_import.py
# scripts/boot_menu.py
scripts/connectors/README.md
scripts/connectors/botify.py
# scripts/connectors/confluence.py
# scripts/connectors/gmail.py
# scripts/connectors/gsc.py
# scripts/connectors/jira.py
scripts/connectors/mcp.py
scripts/connectors/mcp_warm.py
# scripts/connectors/sheets.py
# scripts/connectors/slack.py
# scripts/connectors/wallet.py
# scripts/mother_cat.py
# scripts/sources_menu.py
# scripts/walk.py
# scripts/walk_cartridge.py
# scripts/walk_compile.py
# scripts/weblogin.py
# tools/scraper_tools.py

#    # # adhoc.txt -- Cleanup inert public_walk environment export block
#    # 
#    # # --- BEFORE/AFTER STRADDLE ---
#    # ! grep -n -C 2 'PIPULATE_TRAIL_WALK_ONE_URL' assets/installer/mck.sh || echo "export_block_removed"
#    # ! bash -n assets/installer/mck.sh; echo "mck_syntax=$?"
#    # ! bash assets/installer/mck.sh --where
#    # 
#    # # --- TARGET SCRIPT ---
#    # assets/installer/mck.sh
#    # 
#    # ! rg -n -F 'TRAIL_NAME="${TRAIL_NAME:-public_walk}"' assets/installer/mck.sh
#    # ! rg -n -e '^ *walk\(\)' -e '^ *alias walk=' flake.nix; echo "shell_walk_exit=$?"
#    # ! test -e walk; echo "root_walk_exists=$?"
#    # ! rg -n -e 'alias mothercat=' -e 'Three words to start from' flake.nix scripts/boot_menu.py
#    # flake.nix
#    # scripts/boot_menu.py
#    # assets/installer/mck.sh
#    
#    # ! bash -n walk; echo "walk_syntax=$?"
#    # ! bash walk --where
#    # ! rg -n -e '^ *alias walk=' -e '^ *walk\(\)' -e 'writeShellScriptBin "walk"' flake.nix; echo "shell_walk_exit=$?"
#    # ! .venv/bin/python -c 'import sys; sys.path.insert(0,"scripts"); import boot_menu as b; print("count=" + b._count_word(len(b.DOOR_TWO_WORDS))); print(" ".join(w for w, _ in b.DOOR_TWO_WORDS))'
#    # walk
#    # flake.nix
#    # scripts/boot_menu.py
#    # assets/installer/mck.sh
#    
#    # ! rg -in "three words|four words|door 2" flake.nix scripts/ assets/ README.md
#    # ! walk --where
#    # walk
#    # flake.nix
#    # scripts/boot_menu.py
#    # assets/installer/mck.sh
#    
#    # ! python scripts/connectors/wallet.py check slack
#    # ! python -c "import os, sys; from pathlib import Path; sys.path.insert(0, '.'); from scripts.connectors import wallet; pairs = wallet._dotenv_pairs(); print('env SLACK_USER_TOKEN:', bool(os.getenv('SLACK_USER_TOKEN')), 'env SLACK_BOT_TOKEN:', bool(os.getenv('SLACK_BOT_TOKEN')), 'vault SLACK_USER_TOKEN:', 'SLACK_USER_TOKEN' in pairs, 'vault SLACK_BOT_TOKEN:', 'SLACK_BOT_TOKEN' in pairs)"
#    # ! python scripts/connectors/wallet.py warm slack --dry-run
#    
# /home/mike/repos/trimnoir/_posts/2026-08-30-terminal-native-mcp-practice.md
# deleteme.txt
# Now that slack is working (on my workspace) we trim back things here to just the stick bug and Mother Cat Kata stuff

# deleteme.txt
# deleteme2.txt

! grep -c -E '(vmx|svm)' /proc/cpuinfo
! lsmod | grep -E '^(kvm|vhost)' || echo "no kvm/vhost modules loaded"
! ls -l /dev/kvm /dev/vhost-vsock 2>&1
! id -nG | tr ' ' '\n' | grep -c '^kvm$'
! readlink -f "$(command -v claude-desktop)"
! tail -n 20 ~/.config/Claude/logs/cowork_vm_node.log 2>&1
/home/mike/repos/nixos/packages.nix
/home/mike/repos/nixos/configuration.nix
/home/mike/repos/nixos/hardware-configuration.nix

3: Patches (experiment): None

4: Prompt: The KVM/vsock/group receipts and the cowork_vm_node.log tail from the NixOS box are in context. Given that this machine will never enable a local hypervisor: (a) confirm from the log which named gate the Linux Claude Desktop build hits at launch, (b) state whether this box can still act as a device bridge for cloud Cowork sessions or whether the Chrome side panel / claude.ai is the only Cowork path from it, and (c) if the aaddrick FHS flake rev pinned in packages.nix is now behind the official apt .deb, say what the delta is without proposing an upgrade patch yet.

5: Deliverables: Knowledge

Fable 5 Extra: Context verified. All six receipts landed, and they answer (a) more sharply than the docs did — let me check one thing about the current Linux build before answering (c).Context verified. The six receipts are the first hard evidence in this thread, and they change the picture: the KVM gate isn’t what’s blocking Cowork on this box.

(a) Which gate the log names. None of the four the docs list. The doc-named gates (QEMU missing, no hardware virtualization, no /dev/kvm permission, no vhost_vsock) are UI strings on the Cowork tab; the log records something coarser and earlier: cleanupVMBundleIfUnsupported: yukonSilver not supported (status=unsupported), then Bundle:status rootfs.img missing (the VM image was never downloaded because the platform was already judged unsupported), then startVM: VM not supported (linux/x64), skipping. That is byte-for-byte the signature in the Linux-beta bug report, whose reporter had VT-x enabled, /dev/kvm present and openable by the app’s user, and still got “VM not supported (linux/x64), skipping” with the rootfs bundle cleaned up as unsupported. So on 1.24012.0 the verdict is upstream of any hardware probe: a platform support flag says no, and your firmware setting is never consulted. The helper itself is fine — cowork-linux-helper spawns and listens on /run/user/1000/claude-cowork-vm.sock every launch; only the VM behind it is refused.

The other receipts pin the hardware state precisely, for the record: /proc/cpuinfo shows no vmx/svm (modern kernels clear the flag when firmware disables VT-x, so this reads as firmware-off, matching your intent), no kvm module, no /dev/kvm, no kvm group. One surprise: /dev/vhost-vsock exists and is world-writable with no vhost module loaded, meaning vsock is built into the NixOS kernel. So of the docs’ four gates, only KVM is actually closed on this box — and this build never reaches it.

(b) Can this box bridge cloud sessions? Unwitnessed by these receipts; the tail has no link-related line. The only evidence is the same bug report: on that log signature, cloud-mode sessions were expected to link to the computer without the “Couldn’t link this session to a computer” banner, and didn’t. Same signature, older build (1.18286.2), so the honest reading is “the one documented case with your log coupled them,” not “your box can’t.” The decisive falsifier is interactive and not !-safe: open a cloud Cowork session on claude.ai and see whether the Linux machine is offered as a linkable computer. Practically, with KVM staying off, treat this box as a Cowork client — Chrome side panel or claude.ai, sandbox on Anthropic’s side, nothing local reachable. That’s the same posture the rest of your config expresses, and losing the bridge costs you nothing you wanted.

(c) The flake delta. Your pin 3755cc45… wraps 1.24012.0 (the store path in the log confirms it). Upstream aaddrick has since shipped 1.24012.9, 1.24012.11, 1.26832.0, 1.28929.0, 1.30096.1, 1.30096.5, and 1.32885.1 (Aug 19), the latest tag being v3.2.2+claude1.32885.1. Anthropic’s own apt pool is likely further along (your Mac is 1.40609.0, Aug 27); the probe below reads it directly. Two things in that delta matter for you:

  1. The Linux VM flag flipped. Anthropic’s Linux doc (dated ~Aug 27) now describes Cowork on Linux running in a QEMU/KVM VM with the four named gates, so on a current build this box would get an explicit “Cowork requires hardware virtualization (KVM)” instead of a silent skip. Since KVM stays off, the outcome is identical; only the message improves.
  2. The wrapper went 3.2.1 → 3.2.2, and its --doctor now reports Cowork readiness — which isolation backend will be used, and which of KVM, QEMU, vsock, socat, virtiofsd, and bubblewrap are present. The mention of bubblewrap as a backend alongside KVM is worth one probe on your current install, because if the launcher has a non-KVM isolation path, that’s a fact about your options even if you never take it.

Whether a newer build decouples the cloud device-link from the VM is unwitnessed in any source I found. No upgrade patch, as you asked.


1. PROBES (read-only; BEFORE readings)

timeout 20 claude-desktop --doctor 2>&1 | head -n 40
grep -ciE 'kvm|vmx|/dev/kvm|virtualiz' ~/.config/Claude/logs/cowork_vm_node.log
grep -oiE 'yukonSilver[^,]{0,40}|VM not supported[^,]{0,30}' ~/.config/Claude/logs/cowork_vm_node.log | sort | uniq -c
ls -1 ~/.config/Claude/logs/
grep -liE "couldn't link|link.*computer|device.?bridge" ~/.config/Claude/logs/*.log 2>/dev/null | head -n 5
git ls-remote --tags https://github.com/aaddrick/claude-desktop-debian 2>/dev/null | grep -oE 'v[0-9.]+\+claude[0-9.]+$' | sort -V | tail -n 3
curl -fsS https://downloads.claude.ai/claude-desktop/apt/stable/dists/stable/main/binary-amd64/Packages | grep -E '^Version:' | sort -V | tail -n 1

Line 1 is the one that can falsify (a): if the aaddrick launcher’s doctor names a Cowork isolation backend or a KVM/QEMU check on this install, the “no hardware probe ever runs” reading is wrong. The timeout 20 is a guard in case the FHS wrapper doesn’t know --doctor and tries to launch the GUI instead; with the app already running that would only focus the existing window. Line 2 counts hardware-check mentions in the VM log — 0 supports “gated upstream of hardware,” nonzero refutes it. Line 3 tallies the unsupported-verdict strings so the AFTER reading can show whether they change at all (they shouldn’t; this is the control). Lines 4–5 look for any device-link trace across the other logs to give (b) a non-interactive foothold; empty output is itself the receipt that this build logs no link attempt. Line 6 reads the current aaddrick tag from the wire so the delta in (c) rests on a receipt rather than a search snippet. Line 7 reads Anthropic’s own apt pool for the latest Linux version, which is the number the flake is actually behind.

2. NEXT CONTEXT (adhoc.txt lines, copy-symmetric with the probes)

! timeout 20 claude-desktop --doctor 2>&1 | head -n 40
! grep -ciE 'kvm|vmx|/dev/kvm|virtualiz' ~/.config/Claude/logs/cowork_vm_node.log
! grep -oiE 'yukonSilver[^,]{0,40}|VM not supported[^,]{0,30}' ~/.config/Claude/logs/cowork_vm_node.log | sort | uniq -c
! ls -1 ~/.config/Claude/logs/
! grep -liE "couldn't link|link.*computer|device.?bridge" ~/.config/Claude/logs/*.log 2>/dev/null | head -n 5
! git ls-remote --tags https://github.com/aaddrick/claude-desktop-debian 2>/dev/null | grep -oE 'v[0-9.]+\+claude[0-9.]+$' | sort -V | tail -n 3
! curl -fsS https://downloads.claude.ai/claude-desktop/apt/stable/dists/stable/main/binary-amd64/Packages | grep -E '^Version:' | sort -V | tail -n 1
/home/mike/repos/nixos/packages.nix

The previous six probes are banked and can be retired from adhoc.txt; their receipts are in this thread.

3. PATCHES

No repo patches required. The operator explicitly declined an upgrade patch, and the config already expresses the intended state. No ignition required.

4. PROMPT (caboose copy for prompt.md)

The doctor output, VM-log tallies, other-log link grep, and the wire-read aaddrick tag and Anthropic apt Version are in context. Using only those receipts: (a) confirm or refute that Claude Desktop 1.24012.0 refuses the Linux VM upstream of any hardware probe, (b) say whether this install exposes a non-KVM Cowork isolation backend (bubblewrap) and what that would and would not keep on-device, and (c) state the exact version gap between the pinned flake rev and Anthropic's apt pool. Still no upgrade patch; this box keeps KVM off.

5. EXTERNAL DELIVERABLES

None. The one manual check that isn’t !-safe is the interactive one from (b): start a cloud Cowork session on claude.ai and note whether the Linux machine appears as a linkable computer. Paste the result as prose next turn if you run it.

MikeLev.in:

1: Probe (the before “read”):

(nix) pipulate $ timeout 20 claude-desktop --doctor 2>&1 | head -n 40
grep -ciE 'kvm|vmx|/dev/kvm|virtualiz' ~/.config/Claude/logs/cowork_vm_node.log
grep -oiE 'yukonSilver[^,]{0,40}|VM not supported[^,]{0,30}' ~/.config/Claude/logs/cowork_vm_node.log | sort | uniq -c
ls -1 ~/.config/Claude/logs/
grep -liE "couldn't link|link.*computer|device.?bridge" ~/.config/Claude/logs/*.log 2>/dev/null | head -n 5
git ls-remote --tags https://github.com/aaddrick/claude-desktop-debian 2>/dev/null | grep -oE 'v[0-9.]+\+claude[0-9.]+$' | sort -V | tail -n 3
curl -fsS https://downloads.claude.ai/claude-desktop/apt/stable/dists/stable/main/binary-amd64/Packages | grep -E '^Version:' | sort -V | tail -n 1
0
      1 VM not supported (linux/x64)
      6 yukonSilver not supported (status=unsupported)
chrome-native-host.log
claude.ai-web.log
cowork_vm_node.log
main1.log
main.log
ssh.log
unknown-window.log
v3.2.2+claude1.37937.1
v3.2.2+claude1.37937.3
v3.2.2+claude1.40609.0
Version: 1.40609.0
(nix) pipulate $

Yes, the Claude desktop (Electron) app did pop up but I didn’t know what to do there. Is there something I should check? I went to the cloud version of Claude claude.ai and looked for the project there and it’s not there. But oddly I went to Claude desktop on the Mac and when to that same Projects link in the left-rail and don’t see any listed even in the desktop app! But whenever creating a new discussion there is the project right there set as the default (because I starred it) right under the prompt textarea on a dropdown menu with the option to “Manually approve” or “Automatically approve” so it’s like the desktop version has a project but it’s not shown in any of the platforms when you click “Projects” from the left-rail, but on the Mac where Claude desktop is installed, it shows the project as a dropdown menu seemingly context-setting thing that “feels” like a project but has no clarifying language around it. Maybe these are 2 different things or I’m getting caught in the timing switch-over from projects being local-based to cloud-based? Ohhh! I just noticed on the Linux version it says “Project or folder” on the dropdown menu under the prompt textarea so it’s feeling light there might be folders that can set context without it being “cloud upgraded” (in some as-yet unknown to me way) into a project.

Disentangling Projects and Working Context

2: Context (the after “read”):

! timeout 20 claude-desktop --doctor 2>&1 | head -n 40
! grep -ciE 'kvm|vmx|/dev/kvm|virtualiz' ~/.config/Claude/logs/cowork_vm_node.log
! grep -oiE 'yukonSilver[^,]{0,40}|VM not supported[^,]{0,30}' ~/.config/Claude/logs/cowork_vm_node.log | sort | uniq -c
! ls -1 ~/.config/Claude/logs/
! grep -liE "couldn't link|link.*computer|device.?bridge" ~/.config/Claude/logs/*.log 2>/dev/null | head -n 5
! git ls-remote --tags https://github.com/aaddrick/claude-desktop-debian 2>/dev/null | grep -oE 'v[0-9.]+\+claude[0-9.]+$' | sort -V | tail -n 3
! curl -fsS https://downloads.claude.ai/claude-desktop/apt/stable/dists/stable/main/binary-amd64/Packages | grep -E '^Version:' | sort -V | tail -n 1
/home/mike/repos/nixos/packages.nix

3: Patches (the experiment between the reads): None

4: Prompt: The doctor output, VM-log tallies, other-log link grep, and the wire-read aaddrick tag and Anthropic apt Version are in context. Using only those receipts: (a) confirm or refute that Claude Desktop 1.24012.0 refuses the Linux VM upstream of any hardware probe, (b) say whether this install exposes a non-KVM Cowork isolation backend (bubblewrap) and what that would and would not keep on-device, and (c) state the exact version gap between the pinned flake rev and Anthropic’s apt pool. Still no upgrade patch; this box keeps KVM off.

5: Deliverables: Having a clue.

Fable 5 Extra: Context verified. Receipts settle (a) and (c); (b) came back as “the probe never ran,” which is itself the finding. Your Projects observation is the more useful thread, so it gets the second half.

(a) Confirmed. Zero hits for kvm/vmx//dev/kvm/virtualiz in cowork_vm_node.log; six yukonSilver not supported (status=unsupported) verdicts across three launches; one VM not supported (linux/x64) at the single startVM attempt. The log never records a hardware probe of any kind, and the identical signature appeared on a machine where VT-x was enabled and /dev/kvm was present and openable. On 1.24012.0 the refusal is a platform flag evaluated before any KVM check. Your firmware setting is not consulted.

(b) Unwitnessed, because --doctor isn’t in this rev. No stdout in 2.2s and the GUI appeared: the pinned wrapper doesn’t know the flag, so it went straight to Electron, which ignores unknown switches and launched the app. One false lead to kill now: the -bwrap suffix on your store path is nixpkgs’ buildFHSEnv wrapper — a writeShellScript that invokes bubblewrap to build the FHS namespace the Electron binary runs in. It’s how the app launches, not a Cowork sandbox. If a bubblewrap Cowork backend existed on this rev it would only affect on-computer sessions (local agent loop and local shell in a namespace instead of a VM: shared kernel, weaker isolation, model calls still leave the box). Cloud sessions wouldn’t touch it. Whether the launcher at this rev carries one is what the first two probes below read.

(c) The exact gap. Pin wraps 1.24012.0. Anthropic’s apt pool: Version: 1.40609.0. aaddrick’s newest tag: v3.2.2+claude1.40609.0 — at parity with Anthropic, and the same build number as your Mac. Between the pin and there: 1.24012.9, .11, 1.26832.0, 1.28929.0, 1.30096.1, .5, 1.32885.1, 1.37937.1, .3, 1.40609.0 — ten wrapper releases, with the wrapper itself going 3.2.1 → 3.2.2. No upgrade patch.

The Projects puzzle: two objects wearing one word. You’re right, and the docs are mid-switchover themselves. One help-center article says projects are available on every surface and that projects tied to a local folder support Cowork sessions on desktop only; the Cowork projects article says projects are desktop-only and stored locally, with no cloud sync for project data. Both are marked updated this week. Read your four observations against that:

  1. Left-rail “Projects” is the account-level object: instructions plus knowledge files, synced to your account, visible on claude.ai, mobile, and both desktops. Your starred item isn’t one, which is why it’s absent from the Mac’s left rail too — not just from Linux and the web.
  2. The under-prompt selector is the Cowork working context: a connected local folder, or a local folder-bound Cowork project. It lives in the desktop app’s state on the machine where it was made. Linux labels it “Project or folder” (the older build’s wording), and yours reads “No matches” because nothing was ever connected there.
  3. “Manually approve / Automatically approve” is the session’s permission mode, not a project property — the docs describe Manual, Auto, and Skip (formerly “Act without asking”), with Auto applying to connectors, plugins, the browser, and Claude in Chrome.
  4. “Cloud upgraded” — no documented arrow points up. The two documented creation paths are “Use an existing folder on your computer” (folder → local project) and “Import from a Claude project” (cloud project → local project, transferring files and instructions). Down, never up. Your folder stays a folder.

A correction to my last turn: I said with KVM off the Cowork tab is gated at launch so there’s no Cowork for projects to live in. Your Linux dropdown refutes that. The Cowork surface is present on 1.24012.0; what’s absent is the VM behind on-computer sessions, which matches the architecture FAQ: Cowork continues running file and web tools while the VM is unavailable, and only shell and code execution report the workspace as unavailable.

What to look at when the app pops up. Since the doctor doesn’t exist here, the GUI is the doctor. Three read-only glances answer (b) better than any log: whether Cowork mode offers a “run in the cloud / on your computer” chooser; whether “folder” in the selector opens a native picker; and what Settings → Cowork lists. The decisive bridge test at zero exposure is to connect an empty scratch folder, start a cloud session, and ask Claude to list it — “listed” means the device bridge works without a VM on this build; “Couldn’t link this session to a computer” means the bug report’s coupling holds on 1.24012.0 too. The scratch folder is a car in PATCHES so the mutation rides where mutations ride.


1. PROBES (read-only; BEFORE readings)

find /nix/store/8ir6v1idn0fb4w31bkp8pfr5aph63ghz-claude-desktop-1.24012.0 -maxdepth 3 -type f -size -200k \( -name '*.sh' -o -path '*/bin/*' \) 2>/dev/null | head -n 10
find /nix/store/8ir6v1idn0fb4w31bkp8pfr5aph63ghz-claude-desktop-1.24012.0 -maxdepth 3 -type f -size -200k \( -name '*.sh' -o -path '*/bin/*' \) -exec grep -lE 'doctor|bwrap|bubblewrap' {} + 2>/dev/null | head -n 5
head -c 80 "$(readlink -f "$(command -v claude-desktop)")" | tr -c '[:print:]\n' '.'; echo
ls -1 ~/.config/Claude/ | head -n 40
grep -oiE 'device[ _-]?(regist|link|bridge)[a-z_]*|remote[ _-]?session[a-z_]*' ~/.config/Claude/logs/main.log 2>/dev/null | sort | uniq -c | sort -rn | head -n 10

Line 1 lists the small text launchers inside the inner package (the store path the VM log named), bounded by depth and size so app.asar and the Electron binary are never read. Line 2 greps only those for doctor and bubblewrap: an empty result confirms this rev has neither the doctor nor a bwrap Cowork backend; a hit names the file to read next turn. Line 3 prints the first 80 printable bytes of the outer -bwrap wrapper — a bash shebang settles that it’s nixpkgs’ FHS script and not a Cowork component. Line 4 lists filenames only under the app’s config dir, looking for whatever holds the “Project or folder” selector’s state; names only, nothing catted, so nothing sensitive rides the compile lane. Line 5 tallies device-registration and remote-session tokens in main.log to give (b) a non-interactive foothold; the earlier grep for link failures was empty, so this reads for link attempts.

2. NEXT CONTEXT (adhoc.txt lines, copy-symmetric with the probes)

! find /nix/store/8ir6v1idn0fb4w31bkp8pfr5aph63ghz-claude-desktop-1.24012.0 -maxdepth 3 -type f -size -200k \( -name '*.sh' -o -path '*/bin/*' \) 2>/dev/null | head -n 10
! find /nix/store/8ir6v1idn0fb4w31bkp8pfr5aph63ghz-claude-desktop-1.24012.0 -maxdepth 3 -type f -size -200k \( -name '*.sh' -o -path '*/bin/*' \) -exec grep -lE 'doctor|bwrap|bubblewrap' {} + 2>/dev/null | head -n 5
! head -c 80 "$(readlink -f "$(command -v claude-desktop)")" | tr -c '[:print:]\n' '.'; echo
! ls -1 ~/.config/Claude/ | head -n 40
! grep -oiE 'device[ _-]?(regist|link|bridge)[a-z_]*|remote[ _-]?session[a-z_]*' ~/.config/Claude/logs/main.log 2>/dev/null | sort | uniq -c | sort -rn | head -n 10
/home/mike/repos/nixos/packages.nix

The seven probes from this compile are banked; retire them from adhoc.txt.

3. PATCHES

No repo patches required, and no upgrade patch by your instruction.

Car 1 (optional, off-repo, only if you want the bridge test): creates the empty scratch folder to connect in the Linux app’s “Project or folder” selector. Nothing in it, so nothing can leave.

mkdir -p ~/cowork-scratch

No ignition required: the folder is read by the app only when you pick it in the GUI.

4. PROMPT (caboose copy for prompt.md)

The launcher greps, the outer-wrapper head, the ~/.config/Claude listing, and the main.log device/session tallies are in context, plus whatever I observed in the Linux app's Cowork mode (chooser, folder picker, Settings → Cowork) and, if I ran it, the scratch-folder bridge test. Using only those: (a) state whether rev 3755cc45 carries a --doctor or a bubblewrap Cowork backend at all, (b) say which file under ~/.config/Claude most plausibly holds the "Project or folder" state and what a names-only listing of it would prove, and (c) give the final verdict on whether this KVM-off box can bridge a cloud session. Still no upgrade patch.

5. EXTERNAL DELIVERABLES

Manual observations only, nothing to paste into a file:

  • Linux app (already running): note whether Cowork mode shows a cloud/on-computer chooser; whether “folder” in “Project or folder” opens a native picker; what Settings → Cowork lists. If you ran Car 1, connect ~/cowork-scratch, start a session, ask for a directory listing, and record the exact banner or result.
  • Mac (optional): the local project store lives with the app’s data, not your account — a names-only ls of ~/Library/Application Support/Claude/ on the Mac will show the file the selector reads. Paste filenames as prose next turn; don’t paste contents.

MikeLev.in:

1: Probe (the before “read”):

Blast Radius Check to establish bisection Left-hand Causal Boundary. It is a Popper-thing. Science.
On branch main
Your branch is up to date with 'origin/main'.

nothing to commit, working tree clean
(nix) pipulate $ find /nix/store/8ir6v1idn0fb4w31bkp8pfr5aph63ghz-claude-desktop-1.24012.0 -maxdepth 3 -type f -size -200k \( -name '*.sh' -o -path '*/bin/*' \) 2>/dev/null | head -n 10
find /nix/store/8ir6v1idn0fb4w31bkp8pfr5aph63ghz-claude-desktop-1.24012.0 -maxdepth 3 -type f -size -200k \( -name '*.sh' -o -path '*/bin/*' \) -exec grep -lE 'doctor|bwrap|bubblewrap' {} + 2>/dev/null | head -n 5
head -c 80 "$(readlink -f "$(command -v claude-desktop)")" | tr -c '[:print:]\n' '.'; echo
ls -1 ~/.config/Claude/ | head -n 40
grep -oiE 'device[ _-]?(regist|link|bridge)[a-z_]*|remote[ _-]?session[a-z_]*' ~/.config/Claude/logs/main.log 2>/dev/null | sort | uniq -c | sort -rn | head -n 10
/nix/store/8ir6v1idn0fb4w31bkp8pfr5aph63ghz-claude-desktop-1.24012.0/bin/claude-desktop
#!/nix/store/v8llyqw71lygr2llhmcc8ya5bdlzq45v-bash-5.3p9/bin/bash
ignored=(/nix 
ant-device-registry.json
ant-did
blob_storage
ca-bundle.pem
Cache
claude-code
claude_desktop_config.json
Code Cache
config.json
Cookies
Cookies-journal
cowork-enabled-cli-ops.json
Crashpad
DawnGraphiteCache
DawnWebGPUCache
Dictionaries
DIPS
DIPS-wal
extensions-blocklist.json
fcache
git-worktrees.json
GPUCache
IndexedDB
local-agent-mode-sessions
Local State
Local Storage
logs
Network Persistent State
Partitions
plan-usage-history.json
Preferences
sentry
Session Storage
Shared Dictionary
shared_proto_db
SharedStorage
SharedStorage-wal
SingletonCookie
SingletonLock
SingletonSocket
    310 device-registry
     23 RemoteSessionFolderGrants
      1 device registered
(nix) pipulate $

2: Context (the after “read”):

# adhoc.txt    _   _   _ to set context____ _   _  ___  ____  _   Simpson Couch Gag Here (explain anything to the audience you feel needs it explained)
#     / \   __| | | | | | ___   ___   / ___| | | |/ _ \|  _ \| |  
# ahe/ _ \ / _` | | |_| |/ _ \ / __| | |   | |_| | | | | |_) | |  We don't really have to keep going. Let's wrap this up.
# ahc ___ \ (_| | |  _  | (_) | (__  | |___|  _  | |_| |  __/|_|  
#  /_/   \_\__,_| |_| |_|\___/ \___|  \____|_| |_|\___/|_|   (_)  
# Ad Hoc CHOP: The Not-Managed-by-Git Safe-for-Client-Data place  

# OPTIONAL BUT BIG FOR FULL CONTEXT-WINDOW STORYTELLING
# ! python scripts/articles/lsa.py -t 1 --reverse --fmt dated-slugs  # <-- The "Rolling Pin" that gives the 40K foot book-spine view of book-ore.
# GLOSSARY.md                 # <-- I think this glossary goes well with the book-ore spine to do world building.
# scripts/articles/lsa.py     # <-- Useful for refining commands like `posts`, critical to Second Brain concept.
# ~/repos/nixos/autognome.py  # <-- Letting the AIs really understand my environment (The Brave Little Tailor punches above Their Weight Class proving the dunning-kruger effect the gate-keeper's (lower-case) lament.)
# init.lua                    # <-- Daily driver hot-keys that overlap with aliases in flake.nix
 
# STILL BIG BUT LESS OPTIONAL (especially flake.nix)
# flake.nix                   # <-- THE ONE BIG THING TO INCLUDE Infrastructure as Code (IaC) tells LLM about your system down to the metal
# prompt_foo.py               # <-- This very content-compiling system
# foo_files.py                # <-- This is the router, evolving book outline and the things you pin-up to produced the recursive self-improvement loops

# TINY ILLUMINATING (OK to include every time / automatically = `apply.py`, `.gitignore`, `.gitattributes`)
# requirements.in             # <-- All known dependencies and (necessary) version pinning. WORA gotcha's exposed.
# __init__.py                 # <-- Master versioning
# pyproject.toml              # <-- The PyPI Packaging details

# OPTIONAL ACTUATORS (cheap and good to include to expand the AI's capabilities)
# cli.py                      # <-- Catch-all actuator for PyPI envs, Python anchoring, MCP tool-call (plus alternatives) and **kwargs like wrapping for CLI
# scripts/xp.py               # <-- Transforms host OS copy-paste buffer player-piano music into context-payload.
# scripts/ai.py               # <-- How I constantly use local AI to write git commit messages with `m` alias.
# scripts/crawl.py            # <-- Feel free to ask for something to be crawled and included in the next turn.
# scripts/weblogin.py         # <-- Lets the user "warm up" the cache for their web logins at their leisure on a profile that persists.
 
# MISCELLANEOUS (rare to include but sometimes critical)
# scripts/foo_cartridge.py    # Needs description
# scripts/foo_replay.py       # Needs description
# release.py                  # <-- How everything ends up where it does (GitHub, PyPI, etc.)
# imports/voice_synthesis.py  # <-- The wand can talk to you
# imports/ascii_displays.py   # <-- Where all the ASCII Art lives
# scripts/release/version_sync.py  # <-- Needs to be wrapped into release.py and eliminated, I think.

#                         --- Under this line is were you paste what the AI gives you ---
#                         --- We call it context but it's really just the right-hand  ---
#                         --- blast-radius of the "probes" to make this all science.  ---

# --- END `adhoc.txt` TEMPLATE ---

# STICKBUG & MOTHER CAT KATA
# assets/trails/botify_pageworkers.yaml
# assets/installer/mck.sh
# assets/installer/replay.sh
# assets/trails/first_context.yaml
# assets/trails/practice.yaml
# assets/trails/public_walk.yaml
# scripts/bookmark_import.py
# scripts/boot_menu.py
scripts/connectors/README.md
scripts/connectors/botify.py
# scripts/connectors/confluence.py
# scripts/connectors/gmail.py
# scripts/connectors/gsc.py
# scripts/connectors/jira.py
scripts/connectors/mcp.py
scripts/connectors/mcp_warm.py
# scripts/connectors/sheets.py
# scripts/connectors/slack.py
# scripts/connectors/wallet.py
# scripts/mother_cat.py
# scripts/sources_menu.py
# scripts/walk.py
# scripts/walk_cartridge.py
# scripts/walk_compile.py
# scripts/weblogin.py
# tools/scraper_tools.py

#    # # adhoc.txt -- Cleanup inert public_walk environment export block
#    # 
#    # # --- BEFORE/AFTER STRADDLE ---
#    # ! grep -n -C 2 'PIPULATE_TRAIL_WALK_ONE_URL' assets/installer/mck.sh || echo "export_block_removed"
#    # ! bash -n assets/installer/mck.sh; echo "mck_syntax=$?"
#    # ! bash assets/installer/mck.sh --where
#    # 
#    # # --- TARGET SCRIPT ---
#    # assets/installer/mck.sh
#    # 
#    # ! rg -n -F 'TRAIL_NAME="${TRAIL_NAME:-public_walk}"' assets/installer/mck.sh
#    # ! rg -n -e '^ *walk\(\)' -e '^ *alias walk=' flake.nix; echo "shell_walk_exit=$?"
#    # ! test -e walk; echo "root_walk_exists=$?"
#    # ! rg -n -e 'alias mothercat=' -e 'Three words to start from' flake.nix scripts/boot_menu.py
#    # flake.nix
#    # scripts/boot_menu.py
#    # assets/installer/mck.sh
#    
#    # ! bash -n walk; echo "walk_syntax=$?"
#    # ! bash walk --where
#    # ! rg -n -e '^ *alias walk=' -e '^ *walk\(\)' -e 'writeShellScriptBin "walk"' flake.nix; echo "shell_walk_exit=$?"
#    # ! .venv/bin/python -c 'import sys; sys.path.insert(0,"scripts"); import boot_menu as b; print("count=" + b._count_word(len(b.DOOR_TWO_WORDS))); print(" ".join(w for w, _ in b.DOOR_TWO_WORDS))'
#    # walk
#    # flake.nix
#    # scripts/boot_menu.py
#    # assets/installer/mck.sh
#    
#    # ! rg -in "three words|four words|door 2" flake.nix scripts/ assets/ README.md
#    # ! walk --where
#    # walk
#    # flake.nix
#    # scripts/boot_menu.py
#    # assets/installer/mck.sh
#    
#    # ! python scripts/connectors/wallet.py check slack
#    # ! python -c "import os, sys; from pathlib import Path; sys.path.insert(0, '.'); from scripts.connectors import wallet; pairs = wallet._dotenv_pairs(); print('env SLACK_USER_TOKEN:', bool(os.getenv('SLACK_USER_TOKEN')), 'env SLACK_BOT_TOKEN:', bool(os.getenv('SLACK_BOT_TOKEN')), 'vault SLACK_USER_TOKEN:', 'SLACK_USER_TOKEN' in pairs, 'vault SLACK_BOT_TOKEN:', 'SLACK_BOT_TOKEN' in pairs)"
#    # ! python scripts/connectors/wallet.py warm slack --dry-run
#    
# /home/mike/repos/trimnoir/_posts/2026-08-30-terminal-native-mcp-practice.md
# deleteme.txt
# Now that slack is working (on my workspace) we trim back things here to just the stick bug and Mother Cat Kata stuff

# deleteme.txt
# deleteme2.txt

! find /nix/store/8ir6v1idn0fb4w31bkp8pfr5aph63ghz-claude-desktop-1.24012.0 -maxdepth 3 -type f -size -200k \( -name '*.sh' -o -path '*/bin/*' \) 2>/dev/null | head -n 10
! find /nix/store/8ir6v1idn0fb4w31bkp8pfr5aph63ghz-claude-desktop-1.24012.0 -maxdepth 3 -type f -size -200k \( -name '*.sh' -o -path '*/bin/*' \) -exec grep -lE 'doctor|bwrap|bubblewrap' {} + 2>/dev/null | head -n 5
! head -c 80 "$(readlink -f "$(command -v claude-desktop)")" | tr -c '[:print:]\n' '.'; echo
! ls -1 ~/.config/Claude/ | head -n 40
! grep -oiE 'device[ _-]?(regist|link|bridge)[a-z_]*|remote[ _-]?session[a-z_]*' ~/.config/Claude/logs/main.log 2>/dev/null | sort | uniq -c | sort -rn | head -n 10
/home/mike/repos/nixos/packages.nix

3: Patches (the experiment between the reads): I’m not using the optional path because I don’t want to continue this nose-dive.

4: Prompt: The launcher greps, the outer-wrapper head, the ~/.config/Claude listing, and the main.log device/session tallies are in context, plus whatever I observed in the Linux app’s Cowork mode (chooser, folder picker, Settings → Cowork) and, if I ran it, the scratch-folder bridge test. Using only those: (a) state whether rev 3755cc45 carries a –doctor or a bubblewrap Cowork backend at all, (b) say which file under ~/.config/Claude most plausibly holds the “Project or folder” state and what a names-only listing of it would prove, and (c) give the final verdict on whether this KVM-off box can bridge a cloud session. Still no upgrade patch.

5: Deliverables: More awareness of weird hybrid local-server issues that Claude Cowork uses.

Hop off the ride. This ride’s stated goal is reached — dismount. This is the NOTARY BEAT: the ride ends here, is witnessed here, and is sealed here. Answer all seven beats, briefly:

  1. TLDR: a short, dry, neutral abstract for the TOP of the published article — written for an unfamiliar reader or AI summarizer who has never seen this system. No hype, no insider handles unexplained.
  2. VERIFY: restate the goal from the top of this article and confirm (or deny) it was met, citing THIS compile’s receipts, not memory. Name any ignition this ride required that never fired – an AFTER tap taken without one is a stale BEFORE wearing the AFTER’s label.
  3. BANK: name everything that graduates — rule, earmark, todo, pin — as exact paste-ready lines, plus the exact lines to delete.
  4. DANGLING: what carries forward unbanked? One line each, no essays.
  5. SEED: the adhoc.txt lines (and TODO_SLUGS if narrative context is needed) for the next ride’s first compile.
  6. CLOSING: a closing summary for the BOTTOM of the article — the final take-away, tied to the book’s larger arc where it fits naturally, never forced. Storytelling over inventory.
  7. NOTARIZE: name the sealed artifact of record — the newest hash-stamped cartridge (foo--NN.zip) — and state that its CRC-sealed, byte-reproducible archive is the witnessed receipt of this ride. The seal is the signature; the archive is the deed.

FINALITY: after beat 6, this discussion is CLOSED. Emit NO five-car train, NO probes, NO patches, NO next-turn prompt beyond the SEED lines in beat 4. Any reader or model encountering this article later should treat it as a finished, notarized document — an archive entry, not an open thread.

Fable 5 Extra: Context verified. Dismounting — the five receipts land cleanly, and the ride closes on a firmer footing than it opened.

1. VERIFY

The goal at the top: explain how a browser side panel can offer Cowork if Cowork is a local sandbox — which grew into whether connecting a folder uploads it, why all three platforms demand a hypervisor, and what a KVM-off Linux box can still do. Met, on this compile’s receipts:

  • (a) The inner package’s only small launcher, bin/claude-desktop, contains no doctor, bwrap, or bubblewrap (empty grep). Rev 3755cc45 carries neither a doctor nor a bubblewrap Cowork backend. The outer -bwrap file opens with a Nix bash shebang and ignored=(/nix — nixpkgs’ buildFHSEnv script, not a Cowork component.
  • (b) ~/.config/Claude/ names ant-did, ant-device-registry.json, local-agent-mode-sessions, cowork-enabled-cli-ops.json. The most plausible home for the selector’s state is local-agent-mode-sessions (on-computer sessions, each bound to a folder), with cloud-session grants handled by the RemoteSessionFolderGrants subsystem main.log names 23 times. A names-only listing of that directory would prove whether any session was ever created here; empty would explain “No matches” mechanically. Not read this ride.
  • (c) main.log tallies 310 device-registry, 23 RemoteSessionFolderGrants, 1 device registered. The box is a registered device and the folder-grant machinery runs on a build whose VM is refused — the bridge exists without the hypervisor. The bug report’s coupling (VM unsupported ⇒ session couldn’t link to the computer) is not reproduced by these logs, but end-to-end linking stays unwitnessed: the scratch-folder test was declined.

Ignition: none was required. No patch was applied this ride and every probe was a pure read, so no AFTER tap is wearing a BEFORE’s label. The one optional car (mkdir ~/cowork-scratch) never ran; (c) is therefore “present, not exercised.”

2. BANK

Pin note for packages.nix, paste directly above the claudeFlake = builtins.getFlake line, inside the existing comment block:

  # CLAUDE DESKTOP RIDE SEALED 2026-09-01 (receipts in the sealed cartridge).
  # This pin wraps upstream 1.24012.0; Anthropic's apt pool read 1.40609.0 and
  # aaddrick's newest tag was v3.2.2+claude1.40609.0 (parity with the Mac).
  # LEFT PINNED ON PURPOSE. Cowork on this box: the VM is refused by a platform
  # flag ("yukonSilver not supported", "VM not supported (linux/x64)") BEFORE
  # any KVM probe -- zero kvm/vmx mentions in cowork_vm_node.log -- so firmware
  # VT-x being off is not what gates it. Cloud sessions still run, the device
  # is registered (ant-device-registry.json, "device registered" in main.log),
  # and RemoteSessionFolderGrants is live; only on-computer shell needs KVM.
  # THE -bwrap SUFFIX IS NIX'S buildFHSEnv SCRIPT, not a Cowork sandbox, and
  # this rev's launcher has no --doctor (grep receipt empty). Anthropic's docs
  # name QEMU/KVM/vhost_vsock gates for the CURRENT build, not this one.

Rule, paste-ready for wherever rules live:

# THE FLAG BEATS THE HARDWARE (2026-09-01): a vendor's documented gates are UI
# strings; the log records the verdict that actually fired. Count the log's
# hardware mentions before blaming firmware -- zero means a flag refused first.
# TWO OBJECTS, ONE WORD: "Projects" in the left rail is account-level and synced;
# "Project or folder" under the prompt is machine-local working context. Neither
# arrow points up: folder -> local project, cloud project -> local import, never
# local -> cloud.

Delete from adhoc.txt (banked):

! find /nix/store/8ir6v1idn0fb4w31bkp8pfr5aph63ghz-claude-desktop-1.24012.0 -maxdepth 3 -type f -size -200k \( -name '*.sh' -o -path '*/bin/*' \) 2>/dev/null | head -n 10
! find /nix/store/8ir6v1idn0fb4w31bkp8pfr5aph63ghz-claude-desktop-1.24012.0 -maxdepth 3 -type f -size -200k \( -name '*.sh' -o -path '*/bin/*' \) -exec grep -lE 'doctor|bwrap|bubblewrap' {} + 2>/dev/null | head -n 5
! head -c 80 "$(readlink -f "$(command -v claude-desktop)")" | tr -c '[:print:]\n' '.'; echo
! ls -1 ~/.config/Claude/ | head -n 40
! grep -oiE 'device[ _-]?(regist|link|bridge)[a-z_]*|remote[ _-]?session[a-z_]*' ~/.config/Claude/logs/main.log 2>/dev/null | sort | uniq -c | sort -rn | head -n 10
/home/mike/repos/nixos/packages.nix

3. DANGLING

  • Bridge end-to-end: registered and grants live, never exercised.
  • local-agent-mode-sessions/ never listed; the selector-state file is inferred from names, not read.
  • Whether 1.40609.0 decouples device-link from the VM or merely names the KVM gate in the UI: unwitnessed.
  • Mac ~/Library/Application Support/Claude/ listing not taken; the starred folder-project is known only by UI behavior.
  • Help-center self-contradiction (projects “on every surface” vs “desktop-only, no cloud sync”) is upstream’s to resolve.
  • The pre-existing “UNFREE GATE mechanism unwitnessed” note in packages.nix is untouched by this ride.

4. SEED

adhoc.txt for the next ride’s first compile (back to Stickbug & Mother Cat per the header):

# --- claude-desktop ride sealed 2026-09-01; dormant version-watch, uncomment only when bumping the pin ---
# ! curl -fsS https://downloads.claude.ai/claude-desktop/apt/stable/dists/stable/main/binary-amd64/Packages | grep -E '^Version:' | sort -V | tail -n 1
# ! git ls-remote --tags https://github.com/aaddrick/claude-desktop-debian 2>/dev/null | grep -oE 'v[0-9.]+\+claude[0-9.]+$' | sort -V | tail -n 1
# /home/mike/repos/nixos/packages.nix
scripts/connectors/README.md
scripts/connectors/botify.py
scripts/connectors/mcp.py
scripts/connectors/mcp_warm.py

No TODO_SLUGS needed; the next ride carries its own narrative.

5. CLOSING

It began as a UI oddity — a Cowork button in a browser tab — and ended at the seam where a vendor is running two architectures at once and its own documentation hasn’t finished choosing between them. Every layer of the story was corrected by a cheaper receipt than the one before it. The docs said the tab is hypervisor-gated; your dropdown said otherwise. I said there was no Linux build; your own packages.nix said otherwise. The docs listed four named gates; the log showed a flag standing in front of all of them. The Chrome side panel turned out to be the purest form of the product — a client to a sandbox that is not yours — and the local VM the part that never asked your permission to still exist.

For the book, this is the render-is-not-the-file lesson turned outward: the doc is not the build, the build is not the log, and the log is not the gate. Infrastructure as code let an AI read this machine down to the firmware setting you chose, and probes over trust turned a moving vendor target into dated, witnessed facts. The sovereignty posture — KVM off, narrow folders, the browser as client — survived every receipt. Nothing here asked you to change it, and that is the finding.

Witnessing the Immutable Seam

6. NOTARIZE

The artifact of record is the cartridge this compile produced — foo.zip rotated to the newest foo-<hash8>-NN.zip in the pipulate root, the one bearing the highest NN at the moment of this compile. Its fingerprint from the Summary: prompt_foo.py --chop ADHOC_CHOP --no-tree, 47,116 verified tokens, 181,088 bytes UTF-8; the hash8 is not carried in this payload and is read from the filename. Its CRC-sealed, byte-reproducible archive is the witnessed receipt of this ride. The seal is the signature; the archive is the deed.

This discussion is closed.


Book Analysis

Ai Editorial Take

What is most fascinating about this entry is the psychological comfort developers find in vendor documentation versus the raw truth revealed by inspecting log files. The realization that a software product’s internal platform flags can bypass hardware virtualization checks entirely provides a brilliant cautionary tale for anyone trying to reason about system security and resource utilization purely through UI prompts.

🐦 X.com Promo Tweet

Curious how Claude Cowork handles local files and cloud sandboxes? We dive into terminal logs and hypervisor gates on a KVM-disabled Linux box to see what really happens beneath the hood. Check it out: https://mikelev.in/futureproof/claude-cowork-linux-vm-hypervisor-gates/ #AIArchitecture #Linux #LocalFirst

Title Brainstorm

  • Title Option: The Flag Beats the Hardware: Investigating Claude Cowork, Linux VMs, and Hypervisor Gates in the Age of AI
    • Filename: claude-cowork-linux-vm-hypervisor-gates
    • Rationale: Directly highlights the core technical revelation of the log forensics while remaining highly searchable and professional.
  • Title Option: Demystifying Claude Cowork: Cloud Sandboxes, Local Bridges, and Platform Flags
    • Filename: demystifying-claude-cowork-cloud-sandboxes
    • Rationale: Focuses on the architectural duality of cloud-default execution versus local fallback.
  • Title Option: Probing the Desktop Seam: Claude Cowork Architecture on a KVM-Free Linux Box
    • Filename: probing-the-desktop-seam-claude-cowork
    • Rationale: Emphasizes the practical investigation methodology and the minimalist constraint approach.

Content Potential And Polish

  • Core Strengths:
    • Rigorous use of command-line probes and log forensics to test vendor documentation against reality.
    • Clear distinction between account-level cloud projects and machine-local working context.
    • Maintains a strict adherence to simplicity and minimal-surface principles.
  • Suggestions For Polish:
    • Ensure transitions between conversational dialogue and technical analysis remain smooth for the reader.
    • Highlight the diagnostic commands as reusable patterns for debugging proprietary desktop applications.

Next Step Prompts

  • Analyze how other desktop-hosted AI coding assistants handle local resource delegation without triggering heavy virtualization dependencies.
  • Draft a follow-up methodology guide on auditing closed-source Electron applications using local log files and namespace inspection.