{
  "version": "2026-08-21",
  "basis": "Every tree here is derived from what a surface ARCHITECTURALLY is, not from a security assessment of any vendor. That a command-line agent running under your account can reach what your account can reach is a fact about command-line programs, true of all of them, and it is not a claim about whoever wrote one. Where a node comes from something stronger than that — a published third-party audit, or a direct measurement — the node says so and carries its date.",
  "evidence": {
    "derived": "Reasoned from what the surface is. True by construction, and nobody has checked this instance.",
    "third-party": "From a published read-only audit tool's module list, so the row is comparable with something public.",
    "measured": "Observed inside a running installation on the stated date. One vendor, one surface, one date. Not generalised.",
    "tested": "The boundary was probed by making requests rather than by reading documentation."
  },
  "tiers": {
    "boundary": "Enforced by something outside the grant — the OS, a separate account, a container, a network policy. It holds against a compromised agent.",
    "setting": "Enforced by the tool itself, running inside the grant. Anything that can run code here can go around it.",
    "expectation": "Enforced by nothing. Written in a prompt or a rules file. It is a mandate, not a control.",
    "none": "No mechanism is claimed. This is simply reachable."
  },
  "surfaces": [
    {
      "id": "cli",
      "label": "A command-line agent",
      "where": "your machine",
      "oneline": "Runs under your user account, so it inherits your account's reach.",
      "icon": "terminal"
    },
    {
      "id": "desktop",
      "label": "A desktop application",
      "where": "your machine",
      "oneline": "Also runs as you — the difference from a CLI is which prompts are on by default, and a default is a setting.",
      "icon": "window"
    },
    {
      "id": "web",
      "label": "A hosted assistant",
      "where": "the vendor",
      "oneline": "Reaches what you paste in and whatever connectors you switch on. Not your filesystem.",
      "icon": "cloud"
    },
    {
      "id": "agentbox",
      "label": "A hosted agent container",
      "where": "the vendor",
      "oneline": "A container you do not control, holding the repository you attached to it.",
      "icon": "box"
    }
  ],
  "products": [
    {
      "id": "claude-code",
      "surface": "cli",
      "label": "Claude Code",
      "vendor": "Anthropic"
    },
    {
      "id": "codex-cli",
      "surface": "cli",
      "label": "Codex CLI",
      "vendor": "OpenAI"
    },
    {
      "id": "gemini-cli",
      "surface": "cli",
      "label": "Gemini CLI",
      "vendor": "Google"
    },
    {
      "id": "other-cli",
      "surface": "cli",
      "label": "Another CLI agent",
      "vendor": ""
    },
    {
      "id": "claude-desktop",
      "surface": "desktop",
      "label": "Claude Desktop",
      "vendor": "Anthropic"
    },
    {
      "id": "chatgpt-desktop",
      "surface": "desktop",
      "label": "ChatGPT Desktop",
      "vendor": "OpenAI"
    },
    {
      "id": "other-desktop",
      "surface": "desktop",
      "label": "Another desktop app",
      "vendor": ""
    },
    {
      "id": "claude-web",
      "surface": "web",
      "label": "Claude (web)",
      "vendor": "Anthropic"
    },
    {
      "id": "chatgpt-web",
      "surface": "web",
      "label": "ChatGPT (web)",
      "vendor": "OpenAI"
    },
    {
      "id": "mistral-web",
      "surface": "web",
      "label": "Le Chat (web)",
      "vendor": "Mistral"
    },
    {
      "id": "other-web",
      "surface": "web",
      "label": "Another assistant",
      "vendor": ""
    },
    {
      "id": "claude-code-web",
      "surface": "agentbox",
      "label": "Claude Code on the web",
      "vendor": "Anthropic"
    },
    {
      "id": "other-agentbox",
      "surface": "agentbox",
      "label": "Another hosted agent",
      "vendor": ""
    }
  ],
  "facts": [
    {
      "id": "creds",
      "surfaces": [
        "cli",
        "desktop"
      ],
      "q": "Are there credential files in your home directory?",
      "hint": "Cloud config, kubeconfig, SSH keys, registry tokens — the usual dotfiles.",
      "default": "unsure"
    },
    {
      "id": "cloud",
      "surfaces": [
        "cli",
        "desktop"
      ],
      "requires": [
        "creds"
      ],
      "q": "Do any of them open a cloud or cluster account?",
      "default": "unsure"
    },
    {
      "id": "codehost",
      "surfaces": [
        "cli",
        "desktop"
      ],
      "requires": [
        "creds"
      ],
      "q": "Do any of them push to a code host?",
      "default": "unsure"
    },
    {
      "id": "registry",
      "surfaces": [
        "cli",
        "desktop"
      ],
      "requires": [
        "creds"
      ],
      "q": "Do any of them publish packages or images?",
      "default": "unsure"
    },
    {
      "id": "history",
      "surfaces": [
        "cli",
        "desktop"
      ],
      "q": "Do you keep shell history or previous session transcripts?",
      "hint": "A transcript is a superset of every file that session read.",
      "default": "yes"
    },
    {
      "id": "exec",
      "surfaces": [
        "desktop"
      ],
      "q": "Can it run terminal commands for you?",
      "hint": "Many desktop apps can, through a tool or an extension.",
      "default": "unsure"
    },
    {
      "id": "connectors",
      "surfaces": [
        "web"
      ],
      "q": "Have you connected it to anything — mail, drive, a code host, an MCP server?",
      "default": "unsure"
    },
    {
      "id": "upload",
      "surfaces": [
        "web"
      ],
      "q": "Do you paste or upload work files into it?",
      "default": "yes"
    },
    {
      "id": "repo",
      "surfaces": [
        "agentbox"
      ],
      "q": "Does it have a repository attached?",
      "default": "yes"
    },
    {
      "id": "signing",
      "surfaces": [
        "agentbox"
      ],
      "q": "Is a commit signing identity configured in it?",
      "default": "unsure"
    }
  ],
  "controls": [
    {
      "id": "sep-account",
      "surfaces": [
        "cli"
      ],
      "label": "It runs under a separate user account",
      "effort": "hard — days, and it fights you",
      "tier": "boundary",
      "removes": [
        "creds",
        "hist"
      ],
      "note": "The single largest reduction available, and the reason almost nobody does it is that it is genuinely difficult: paths, permissions, agents and editors all assume one account. Desktop applications frequently cannot be run this way at all."
    },
    {
      "id": "container",
      "surfaces": [
        "cli"
      ],
      "label": "It runs in a container with only the project mounted",
      "effort": "an afternoon, then ongoing friction",
      "tier": "boundary",
      "removes": [
        "creds",
        "hist"
      ],
      "downgrades": {
        "exec": "container"
      },
      "note": "Execution survives inside the container and stops being execution on your machine. The friction is real and recurring — mounts, tokens, editor integration."
    },
    {
      "id": "egress",
      "surfaces": [
        "cli",
        "desktop",
        "agentbox"
      ],
      "label": "Outbound traffic goes through an allowlist",
      "effort": "an hour, if you already have somewhere to put it",
      "tier": "boundary",
      "swaps": {
        "net.any": "net.allowed"
      },
      "note": "The one control the hosted container already has, demonstrated rather than claimed. You can have it too."
    },
    {
      "id": "vault-creds",
      "surfaces": [
        "cli",
        "desktop"
      ],
      "label": "Credentials live somewhere the agent's account cannot open",
      "effort": "an afternoon",
      "tier": "boundary",
      "removes": [
        "creds"
      ],
      "note": "Only helps if the agent cannot become you — pair it with a separate account or a container, or it is a setting wearing a boundary's clothes."
    },
    {
      "id": "no-skip",
      "surfaces": [
        "cli",
        "desktop"
      ],
      "label": "Permission prompts are on, with no skip flag set",
      "effort": "five minutes",
      "tier": "setting",
      "marks": {
        "cfg": "setting"
      },
      "note": "Worth doing and it is NOT a boundary: the prompt is enforced by the tool, and this grant can rewrite the file that disables it. Labelled honestly so you are not misled."
    },
    {
      "id": "no-connect",
      "surfaces": [
        "web"
      ],
      "label": "No connectors are switched on",
      "effort": "immediate",
      "tier": "boundary",
      "removes": [
        "connect"
      ],
      "note": "The connector list is the whole of a hosted assistant's reach beyond the conversation. It is enforced by the vendor and it is genuinely outside the session."
    },
    {
      "id": "less-in",
      "surfaces": [
        "web",
        "agentbox"
      ],
      "label": "Only non-sensitive material goes in",
      "effort": "immediate, and continuous",
      "tier": "expectation",
      "note": "The hosted grant reaches what you put in front of it, and that part IS yours. It is an expectation rather than a boundary because nothing enforces it but you, every time."
    }
  ],
  "nodes": {
    "cli": [
      {
        "id": "root",
        "parent": null,
        "label": "Runs as your user account",
        "tier": "none",
        "evidence": "derived",
        "detail": "This is the grant. Everything below is a consequence of it rather than a separate decision — the agent inherits your account, so it inherits your account's reach."
      },
      {
        "id": "conv",
        "parent": "root",
        "label": "The conversation itself",
        "tier": "none",
        "evidence": "derived",
        "reaches": [
          "chat",
          "draft",
          "explain"
        ],
        "detail": "Everything an agent does starts here, and it is the part nobody thinks of as a grant. It is drawn because a tool that only lists frightening capabilities is measuring its own framing rather than your setup."
      },
      {
        "id": "files",
        "parent": "root",
        "label": "Read and write anything your account can",
        "mechanism": "the tool's own directory restriction",
        "tier": "setting",
        "evidence": "derived",
        "detail": "The folder restriction is enforced by the tool, which runs inside this grant. Anything that can execute as you can step around it — see the escalation edge from “Execute programs as you”."
      },
      {
        "id": "proj",
        "parent": "files",
        "label": "The project you are working on",
        "tier": "none",
        "evidence": "derived",
        "reaches": [
          "read.project",
          "write.project"
        ]
      },
      {
        "id": "creds",
        "parent": "files",
        "label": "Credential files in your home directory",
        "tier": "none",
        "evidence": "third-party",
        "requires": "creds",
        "reaches": [
          "read.credentials"
        ],
        "detail": "A published read-only audit tool enumerates exactly this class: key files and their permissions, cloud and cluster credentials, registry tokens, code-host credentials. Nothing stands in the way, because the files are owned by the account the agent runs as."
      },
      {
        "id": "cloud",
        "parent": "creds",
        "label": "Cloud and cluster accounts",
        "tier": "none",
        "evidence": "third-party",
        "requires": "cloud",
        "reaches": [
          "cloud.api"
        ]
      },
      {
        "id": "code",
        "parent": "creds",
        "label": "Code hosts and signing keys",
        "tier": "none",
        "evidence": "third-party",
        "requires": "codehost",
        "reaches": [
          "git.push",
          "sign.commits"
        ]
      },
      {
        "id": "pkg",
        "parent": "creds",
        "label": "Package and image registries",
        "tier": "none",
        "evidence": "third-party",
        "requires": "registry",
        "reaches": [
          "pkg.publish"
        ]
      },
      {
        "id": "hist",
        "parent": "files",
        "label": "Shell history and past transcripts",
        "tier": "none",
        "evidence": "third-party",
        "requires": "history",
        "reaches": [
          "read.transcripts"
        ],
        "detail": "A session transcript is a superset of every file that session read, so excluding a secret by path does not exclude its contents. Two independent routes reached this conclusion, which is why it is a finding rather than an observation."
      },
      {
        "id": "cfg",
        "parent": "files",
        "label": "Its own configuration and rules files",
        "mechanism": "a permission prompt",
        "tier": "setting",
        "evidence": "third-party",
        "reaches": [
          "alter.limits"
        ],
        "detail": "A prompt that a flag in a writable file can disable is an expectation wearing a setting's clothes. The audit tool named below looks for exactly those flags in shell initialisation."
      },
      {
        "id": "exec",
        "parent": "root",
        "label": "Execute programs as you",
        "mechanism": "an approved-tools list",
        "tier": "setting",
        "evidence": "derived",
        "reaches": [
          "exec.local"
        ],
        "detail": "This is the node that makes the two settings above cosmetic. It is why the escalation edges exist."
      },
      {
        "id": "net",
        "parent": "root",
        "label": "Open outbound connections",
        "tier": "none",
        "evidence": "derived",
        "reaches": [
          "net.any"
        ]
      }
    ],
    "desktop": [
      {
        "id": "root",
        "parent": null,
        "label": "Runs as your user account",
        "tier": "none",
        "evidence": "derived",
        "detail": "A desktop application installed by you runs as you. The difference from the command line is which confirmations are on by default — and a default is a setting."
      },
      {
        "id": "conv",
        "parent": "root",
        "label": "The conversation itself",
        "tier": "none",
        "evidence": "derived",
        "reaches": [
          "chat",
          "draft",
          "explain"
        ],
        "detail": "Everything an agent does starts here, and it is the part nobody thinks of as a grant. It is drawn because a tool that only lists frightening capabilities is measuring its own framing rather than your setup."
      },
      {
        "id": "files",
        "parent": "root",
        "label": "Read and write anything your account can",
        "mechanism": "a workspace-folder restriction",
        "tier": "setting",
        "evidence": "derived",
        "reaches": [
          "read.project",
          "write.project"
        ]
      },
      {
        "id": "creds",
        "parent": "files",
        "label": "Credential files in your home directory",
        "tier": "none",
        "evidence": "third-party",
        "requires": "creds",
        "reaches": [
          "read.credentials"
        ]
      },
      {
        "id": "cloud",
        "parent": "creds",
        "label": "Cloud and cluster accounts",
        "tier": "none",
        "evidence": "third-party",
        "requires": "cloud",
        "reaches": [
          "cloud.api"
        ]
      },
      {
        "id": "code",
        "parent": "creds",
        "label": "Code hosts and signing keys",
        "tier": "none",
        "evidence": "third-party",
        "requires": "codehost",
        "reaches": [
          "git.push",
          "sign.commits"
        ]
      },
      {
        "id": "pkg",
        "parent": "creds",
        "label": "Package and image registries",
        "tier": "none",
        "evidence": "third-party",
        "requires": "registry",
        "reaches": [
          "pkg.publish"
        ]
      },
      {
        "id": "hist",
        "parent": "files",
        "label": "Shell history and past transcripts",
        "tier": "none",
        "evidence": "third-party",
        "requires": "history",
        "reaches": [
          "read.transcripts"
        ]
      },
      {
        "id": "cfg",
        "parent": "files",
        "label": "Its own settings and rules files",
        "mechanism": "a confirmation dialog",
        "tier": "setting",
        "evidence": "derived",
        "reaches": [
          "alter.limits"
        ]
      },
      {
        "id": "exec",
        "parent": "root",
        "label": "Run terminal commands as you",
        "mechanism": "a per-command confirmation",
        "tier": "setting",
        "evidence": "derived",
        "requires": "exec",
        "reaches": [
          "exec.local"
        ]
      },
      {
        "id": "net",
        "parent": "root",
        "label": "Open outbound connections",
        "tier": "none",
        "evidence": "derived",
        "reaches": [
          "net.any"
        ]
      }
    ],
    "web": [
      {
        "id": "root",
        "parent": null,
        "label": "Runs in the vendor's environment",
        "mechanism": "it is not your machine",
        "tier": "boundary",
        "evidence": "derived",
        "detail": "This is the one place a hosted surface is structurally better: your filesystem is not reachable, and the boundary is enforced by something entirely outside the session. You also cannot inspect it, change it, or get it attested."
      },
      {
        "id": "conv",
        "parent": "root",
        "label": "The conversation",
        "tier": "none",
        "evidence": "derived",
        "reaches": [
          "chat",
          "draft",
          "explain"
        ]
      },
      {
        "id": "paste",
        "parent": "root",
        "label": "What you paste or upload",
        "tier": "none",
        "evidence": "derived",
        "requires": "upload",
        "reaches": [
          "read.project"
        ],
        "detail": "This is the part of a hosted grant that is genuinely yours to control, and the only part. Everything you put in is inside the grant for as long as the conversation exists."
      },
      {
        "id": "connect",
        "parent": "root",
        "label": "Connectors you switched on",
        "mechanism": "the connector list",
        "tier": "boundary",
        "evidence": "derived",
        "requires": "connectors",
        "reaches": [
          "read.home",
          "git.push",
          "cloud.api"
        ],
        "detail": "A connector moves a hosted assistant from “reaches what you paste” to “reaches an account”. It is enforced by the vendor and it is outside the session, so it is a real boundary — one you set, and one worth re-reading occasionally."
      },
      {
        "id": "home",
        "parent": "root",
        "label": "Your own machine's files",
        "mechanism": "the vendor's environment is not your machine",
        "tier": "boundary",
        "evidence": "derived"
      }
    ],
    "agentbox": [
      {
        "id": "root",
        "parent": null,
        "label": "Runs as root inside a vendor container",
        "tier": "none",
        "evidence": "measured",
        "detail": "Measured inside one running session on 20 August 2026. Root inside the container, with passwordless escalation — so there is no internal boundary at all. One vendor, one surface, one date, and the vendor is not named because the point is the taxonomy rather than the grade."
      },
      {
        "id": "repo",
        "parent": "root",
        "label": "The repository you attached",
        "tier": "none",
        "evidence": "measured",
        "requires": "repo",
        "reaches": [
          "read.project",
          "write.project",
          "git.push"
        ]
      },
      {
        "id": "sign",
        "parent": "root",
        "label": "A configured commit signing identity",
        "tier": "none",
        "evidence": "measured",
        "requires": "signing",
        "reaches": [
          "sign.commits"
        ]
      },
      {
        "id": "home",
        "parent": "root",
        "label": "Your own machine's files",
        "mechanism": "the container is not your machine",
        "tier": "boundary",
        "evidence": "measured"
      },
      {
        "id": "creds",
        "parent": "root",
        "label": "Credential stores in the image",
        "mechanism": "present, and empty of usable material",
        "tier": "boundary",
        "evidence": "measured"
      },
      {
        "id": "net",
        "parent": "root",
        "label": "Outbound network",
        "mechanism": "an egress allowlist the process cannot edit",
        "tier": "boundary",
        "evidence": "tested",
        "reaches": [
          "net.allowed"
        ],
        "detail": "Probed by making two requests: a package index resolved, an arbitrary public host did not. It is a real boundary for a precise reason — the agent is root, and root cannot defeat it, because nothing inside the container enforces it. This is the only tier-one boundary in the whole library, and it is published because a page that only reports unfavourable findings is doing advocacy rather than measurement."
      }
    ]
  },
  "escalations": [
    {
      "surfaces": [
        "cli",
        "desktop"
      ],
      "from": "exec",
      "to": "cfg",
      "why": "Anything that can run programs as you can rewrite the file that turns the prompt off."
    },
    {
      "surfaces": [
        "cli",
        "desktop"
      ],
      "from": "exec",
      "to": "creds",
      "why": "The folder restriction is enforced by the tool. Execution as you goes around it."
    }
  ],
  "capabilities": [
    {
      "id": "chat",
      "group": "benign",
      "label": "Have a conversation",
      "weight": 0
    },
    {
      "id": "draft",
      "group": "benign",
      "label": "Draft or edit a document for me",
      "weight": 0
    },
    {
      "id": "explain",
      "group": "benign",
      "label": "Explain code or a file I show it",
      "weight": 0
    },
    {
      "id": "read.project",
      "group": "work",
      "label": "Read the project it is working on",
      "weight": 1
    },
    {
      "id": "write.project",
      "group": "work",
      "label": "Change the project it is working on",
      "weight": 1
    },
    {
      "id": "exec.local",
      "group": "work",
      "label": "Run programs as you",
      "weight": 4
    },
    {
      "id": "net.allowed",
      "group": "work",
      "label": "Reach a permitted list of hosts",
      "weight": 1
    },
    {
      "id": "net.any",
      "group": "work",
      "label": "Reach any host on the internet",
      "weight": 3
    },
    {
      "id": "git.push",
      "group": "work",
      "label": "Push to your code hosts",
      "weight": 3
    },
    {
      "id": "sign.commits",
      "group": "work",
      "label": "Sign commits as you",
      "weight": 2
    },
    {
      "id": "pkg.publish",
      "group": "work",
      "label": "Publish packages and images as you",
      "weight": 3
    },
    {
      "id": "cloud.api",
      "group": "work",
      "label": "Act in your cloud and cluster accounts",
      "weight": 4
    },
    {
      "id": "read.home",
      "group": "reach",
      "label": "Read your other files",
      "weight": 3
    },
    {
      "id": "write.home",
      "group": "reach",
      "label": "Change your other files",
      "weight": 3
    },
    {
      "id": "read.credentials",
      "group": "reach",
      "label": "Read your stored credentials",
      "weight": 4
    },
    {
      "id": "read.transcripts",
      "group": "reach",
      "label": "Read your shell history and past sessions",
      "weight": 3
    },
    {
      "id": "alter.limits",
      "group": "reach",
      "label": "Change its own permission settings",
      "weight": 4
    }
  ],
  "examples": [
    {
      "id": "solo-dev",
      "label": "A developer on a laptop",
      "blurb": "One CLI agent, credentials in the home directory, no containment. The common case, and the one with the widest gap.",
      "state": {
        "products": [
          "claude-code"
        ],
        "facts": {
          "creds": "yes",
          "cloud": "yes",
          "codehost": "yes",
          "registry": "no",
          "history": "yes"
        },
        "controls": [],
        "intent": [
          "chat",
          "explain",
          "read.project",
          "write.project"
        ]
      }
    },
    {
      "id": "careful-dev",
      "label": "The same developer, contained",
      "blurb": "The same agent, in a container, with an egress allowlist. Shows what the controls actually remove.",
      "state": {
        "products": [
          "claude-code"
        ],
        "facts": {
          "creds": "yes",
          "cloud": "yes",
          "codehost": "yes",
          "registry": "no",
          "history": "yes"
        },
        "controls": [
          "container",
          "egress"
        ],
        "intent": [
          "chat",
          "explain",
          "read.project",
          "write.project",
          "exec.local"
        ]
      }
    },
    {
      "id": "mixed",
      "label": "A team using three surfaces",
      "blurb": "A CLI on the laptop, a hosted assistant in the browser, and a hosted agent container. Three shapes, one dashboard.",
      "state": {
        "products": [
          "claude-code",
          "chatgpt-web",
          "claude-code-web"
        ],
        "facts": {
          "creds": "yes",
          "cloud": "unsure",
          "codehost": "yes",
          "registry": "unsure",
          "history": "yes",
          "connectors": "yes",
          "upload": "yes",
          "repo": "yes",
          "signing": "yes"
        },
        "controls": [],
        "intent": [
          "chat",
          "draft",
          "explain",
          "read.project",
          "write.project"
        ]
      }
    },
    {
      "id": "hosted-only",
      "label": "No local agents at all",
      "blurb": "Only a hosted assistant, no connectors. The smallest grant in the library — and the one you can least verify.",
      "state": {
        "products": [
          "claude-web"
        ],
        "facts": {
          "connectors": "no",
          "upload": "yes"
        },
        "controls": [
          "no-connect"
        ],
        "intent": [
          "chat",
          "draft",
          "explain",
          "read.project"
        ]
      }
    }
  ],
  "rerun": {
    "cli": "Run a published read-only agent-risk audit against your own machine — e.g. github.com/bartek-filipiuk/ai-agent-audit — and compare its modules with the nodes here. It reports path, type, count and a redacted fingerprint, and never leaves your machine.",
    "desktop": "The same audit covers a desktop install: what matters is which account the application runs under, and whether it can run commands.",
    "web": "Open the connector or integration settings and list what is switched on. That list is the whole of the grant beyond the conversation.",
    "agentbox": "Inside a session, check the process identity, look for an attestation device, and probe egress by requesting one expected host and one arbitrary one. Two requests is the whole test."
  }
}
