Modelling Checklist
Threat Patterns decides a leg only from what your model states. An unanswered attribute or field is undetermined, never read as "no". This checklist lists the facts and drawings the analysis asks for most often. For each one it says where to set it and which leg it settles. Attribute names are shown as they appear on the class, in code.
If you are modelling an agentic system from scratch, start with the training Model an agentic architecture: it teaches these facts in the order to draw them.
Start from the Facts to establish tab. It asks each question once, naming the exact element and fact for your model. Use this page to understand what each one means. Back to the user guide.
Step 1: State the origin of untrusted content
Where: the data item's Origin field (origin).
Set: who can write the content. Third party, Arbitrary public and Not assessed count as untrusted. Authenticated first party, Curated internal, Agent relayed and Model generated do not.
Settles: U (lethal trifecta), untrusted input (injection to action), untrusted content at the delegator (delegation hop).
You do not always need the field. Content carried from an element in an UNTRUSTED zone, or from an Untrusted Content Source or Public AI Artifact Hub, already counts as untrusted. For this to work, the data items must be drawn on the flow. When they are missing, Facts to establish asks you to "Draw the data items on" that flow. What the agent reads back over a Context Ingress from a Vector Database or an Agent Memory Service counts as untrusted when a party in an UNTRUSTED zone writes into it, or when one of its write-access answers, such as memory_write_gated_from_untrusted_content, is false; answered true, those answers settle nothing on their own.
Step 2: Mark credentials and sensitive data
Where: the data item's Credential field (is_credential in a model file), and its Sensitivity.
Set: Credential to Yes — a credential or No. Set Sensitivity on other data. Internal, Confidential and Restricted count as private. Public counts as not private only when Credential is No.
Settles: which data items are credentials, the subjects of credential chain. Also P (lethal trifecta) and delegate holds the rest (delegation hop), because a credential is private data. A credential held by an MCP server with Credential unanswered leaves P undetermined for the agents that call it.
Step 3: Connect each credential to its holder and to what it unlocks
Where: on the canvas.
Draw: add the credential as a data item of the component or boundary that holds it. Put it on the flow that carries it to the system it unlocks.
Settles: reach (holder route) and unlock (credential chain). Without the unlock flow, a chain stays Not ruled out and the rotate list shows nothing under Unlocks.
Optional: on an Agent Credential, answer credential_expiry_enforced, key_rotation_enabled, resource_scope_restricted and permission_scope_minimized. These settle weak credential, which makes a chain worse but never decides it.
Step 4: Answer the agent's tool attributes
Where: the Agent Service element's attributes.
| Attribute | Settles |
|---|---|
code_execution_tools_present | action tools (injection to action). Answered true, the agent can read files in its shared space: P (trifecta), foothold and reach (credential chain), unless code_execution_sandboxed is true. Answered false, it is not asked again, but it does not rule out file reads: running no code is not holding no tool that reads files. Draw the Agent Tool Call to the tool or harness that reads files, if the agent has one |
code_execution_sandboxed | Answered true only when four things hold: the sandbox the agent's code runs in mounts none of the agent's files or working directory, receives none of its secrets or environment variables, holds no cloud or host credentials of its own, and its network cannot reach the cloud metadata endpoint (169.254.169.254) or the agent's own services. The code then reads nothing of the agent's shared space and reaches no metadata endpoint. File reads are then asked as for an agent that runs no code, the question ending "(its code runs sandboxed)": draw the Agent Tool Call to the harness that reads files, if there is one. A drawn call to such a harness still gives the agent file reads |
consequential_tools_present | action tools, and together with code_execution_tools_present, whether W applies at all. It does not only when both are false and no MCP server the agent calls answers destructive_tools_exposed true |
consequential_tools_all_require_approval, approval_gate_present | W (trifecta), no gate (injection to action) |
tool_credentials_least_privilege_user_context | broad identity (injection to action) |
network_egress_default_deny_allowlist | X on the agent's own route (trifecta) |
With consequential_tools_present false, consequential_tools_all_require_approval true cannot settle W absent: it is about consequential tools, not about code execution or a called MCP server's destructive tools.
On the elements and flows around the agent:
| Where | Attribute | Settles |
|---|---|---|
| Local MCP Server, Remote MCP Server | destructive_tools_exposed | action tools through the server |
| Local MCP Server | server_network_egress_restricted | X through the server |
| Context Ingress flow | human_approval_for_actions_after_untrusted_ingress, untrusted_ingress_can_trigger_consequential_actions | no gate, per path (see below) |
| Agent Tool Call flow | human_approval_for_sensitive_tool_calls | no gate, per path (see below) |
| Agent Tool Call flow | agent_egress_restricted | X through the tool call |
| Agent Tool Call flow | handoff_permission_mode_not_widened | open delegation |
| Agent Chat Frontend | markdown_image_rendering_policy | X through the rendered chat |
The paths are every Context Ingress that may carry untrusted content into the agent, and every Agent Tool Call out of it. Their approval answers settle no gate:
| Path answers | No gate |
|---|---|
| Any one path answered unapproved | present |
| Every path answered approved | absent, unless untrusted content also reaches the agent through an MCP server or a knowledge item, the agent runs code with no drawn Agent Tool Call to the harness or executor it runs on, or the agent's own answers say an action runs unapproved (that action is then on a path not drawn) |
| Otherwise | the agent's own answers decide |
Step 5: Draw the flows the agent really has
Where: on the canvas, from and to the agent.
| Draw | Settles |
|---|---|
| Context Ingress flows into the agent, with their data items | U, untrusted input |
| Agent Tool Call flows to the harness, to code executors and to MCP servers | action tools, the shared-space read, and which harness runs the agent |
| Every outbound flow of the agent | X. An agent with none drawn is counted in the model gap "agents have no declared outbound flows" |
| The data items the agent handles | P |
| The agent's tool targets, and their data or crown jewel marking | sensitive target |
| A Context Ingress from agent A into agent B, or an Agent Tool Call from A to B | the delegation hop between them. Both ends must be agents. A Remote A2A Agent can be the sender |
Place outside parties in an UNTRUSTED zone and a vendor's elements in a VENDOR boundary. Zones decide who can read what the agent sends. Who holds each fix is a separate answer; see step 7.
Step 6: Say how the harness approves actions
Where: the Agent Harness element, harness_approval_mode.
Set: the mode the harness runs in. default and plan mean the agent does not write without approval. acceptEdits, auto, dontAsk, bypassPermissions and never mean it does. on-request and granular do not decide; also answer the approval attributes on the agent (section 4). dangerously_skip_permissions_in_use set to true means the agent writes without approval, whatever the mode.
Tie the agent to one harness. Put both in the same Agent Runtime boundary, or draw an Agent Tool Call from the agent to the harness. With several candidates, Facts to establish asks you to draw which harness runs the agent.
Settles: W (trifecta), and the model gaps "W depends on an unanswered harness mode" and "no hosting harness drawn".
Step 7: Say who runs each element
Where: the element's Operated by field, in the last panel of its Attributes tab: Operation & version on a component, Operation on a boundary. The panel shows once the element has a class.
Set: You (your organisation) or A vendor, and only when you know.
- On a component: who builds or runs the element's own software. A hosted service you only configure is A vendor. Something you build or run is You (your organisation), also when you build it on a vendor's platform.
- On a boundary: who runs the environment of what is drawn directly in it. Draw each element directly in the boundary that runs its environment. The canvas itself never counts.
- Product: the vendor and product name, where you know it. A fix the vendor holds names it.
Leave it empty unless you know. Do not guess. An empty field is read from the zone: an element in a VENDOR zone counts as the vendor's, any other as yours. Each fix whose holder depends on it then asks "who holds it depends on: …", so the open answer stays visible. A guess removes the question and puts the fix on the wrong owner.
Settles: who holds each fix: you (a change), you in part through the vendor's settings (partial), or the vendor (compensate or accept). It changes no leg and no finding. A setting is always yours, so a fix on a setting asks nothing. A flow has no field: it counts as the vendor's only when every element it connects is. A data item is always yours. See Fixes on a finding.
Step 8: Use the assertion only where the model cannot say it
Where: Facts to establish, as a second button on a question that offers one.
| Button | Appears when | You state | Settles |
|---|---|---|---|
| Assert outbound flows complete | The element's class has no attribute that could state its egress | Every outbound flow of the element is drawn | X at that element |
| Assert not a hand-off, and B's model never reads it | A flow between agents is drawn so that it may be a hand-off, and the question asks whether it is one or how it scopes permissions. With several receiving agents, the button names them all | The flow hands no work on to B, and B's model never reads what it carries | The routes that rest on a hand-off over that flow, such as the delegation hop |
| Assert H is not assigned to F's compute | The question asks whether identity H serves the compute that F runs on. Each identity gets its own button | H is not assigned to the compute F runs on | reach through H's token (credential chain) |
Write a justification; it is required. The next run settles those routes absent and names your assertion as the evidence. Outbound flows complete still follows the element's drawn flows: an assertion on the element where a route leads in settles only that element, and a dead end or a route continuing past it is still asked about.
An assertion lives on this analysis only. It does not change the model, and a new analysis of the same model starts without it. If what it is on changes, it turns stale and is no longer applied. Prefer an attribute or a drawing whenever the model has one. The dialog, the server's refusals and the Assertions list are in the user guide, under Act on a question.
Step 9: Generate again
After you change the model, the banner reads The model changed since this run. (on a result generated before Threat Patterns engine 0.1.5, The model or Threat Patterns changed since this run.). Click Generate again, then check that the facts you answered have left Facts to establish.
Example. In one model, the Facts to establish tab asked one question, to set code_execution_tools_present on the main agent. It settled two credentials at once. After that one attribute was answered true and the analysis was generated again:
- two credential chains appeared, one Confirmed and one Not ruled out (its unlock flow was not drawn);
- both credentials joined the rotate list;
- new fixes appeared on the Fixes tab;
- the question left Facts to establish.
The Not ruled out chain now has its own open question on Facts to establish, the remaining to settle line: draw the flow that carries the second credential to what it unlocks. That is the next step in that model.
