Autonomous AI agents inside a Qubes-isolated sandbox - tag-scoped Admin API access with dom0-mediated trust boundary.

Summary

A Qubes-isolated sandbox for AI agents — FastMCP server in a dedicated
mcp-control qube — that lets autonomous AI workflows provision qubes,
build templates, run pentests, and move files between them, all inside a
tag-scoped subset of the system. Untagged qubes remain structurally
invisible to the agent.

Stages A and B (below) are tested. Stages C–H are designed in CLAUDE.md.

Repo: GitHub - alex-schose/qubes-mcp: Autonomous AI agents inside a Qubes-isolated sandbox - tag-scoped Admin API access with dom0-mediated trust boundary. · GitHub

Design summary

  • Trust boundary = the qrexec tag ai-managed. Tag mutation is
    hard-denied to AI at the policy layer; only the operator (in dom0) and
    the create-time wrapper qmcp.SpawnAIManagedQube apply tags.
  • Dom0-mediated wrappers (qmcp.*). State-changing calls route through
    small Python scripts in /etc/qubes-rpc/ that enforce invariants in
    dom0 before touching qubesd: forced tagging on creation, cross-reference
    validation on template/netvm/default_dispvm, opaque error responses.
  • Wrapped reads hide existence. qmcp.GetPropertyAIManaged returns
    "not found" indistinguishably whether the qube doesn’t exist or simply
    isn’t tagged. The MCP-side helper normalises all qrexec failures (policy
    deny, no-such-VM, transport error) to the same opaque error so the
    lifecycle path doesn’t leak either.

Status

  • Stage A (tested) — tag-scoped lifecycle, spawn (atomic tag-on-create
    with rollback + post-condition check), wrapped property read/write,
    existence hiding.
  • Stage B (tested) — root command execution and inter-qube file
    transfer inside ai-managed qubes via custom qrexec services installed
    in ai-managed templates.
  • Stages C–H (designed) — network sandbox + tag-validated netvm cascade,
    template cloning + DispVMs, device attach between ai-managed qubes,
    feature.Set wrapper + filtered event stream, mcp-control hardening +
    Tor hidden service for sshd, FastMCP HTTP/SSE transport bound to a
    second .onion for mobile-app reach.

Three review questions

For anyone familiar with the Admin API and qrexec policy R4.2+:

  1. Wrapped-reads existence-hiding. Is returning a uniform "not found"
    from a dom0 wrapper a robust primitive against existence oracles, or
    are there qrexec-layer leaks (timing, error chains, side effects)
    I’m missing?

  2. qubes.Filecopy between @tag:ai-managed qubes. Stage B adds a
    policy line bypassing the default ask dialog for transfers between
    ai-managed qubes. Are there assumptions in qubes.Filecopy’s
    implementation that depend on the dialog being present?

  3. target=@adminvm documentation gap. Without that clause on
    tag-scoped admin allows, qrexec attempts to start the target VM during
    read-only operations. Subtle, easy to miss, not surfaced in current
    docs. Worth a docs PR? Happy to write it.

Disclosure

AI-assisted implementation, human-designed boundaries. Not classical-engineer
credentialed but seriously trying to get the threat model right. Review
will find things I missed — that’s why this post is here.

— alex-schose