# Threat model

## Assets and boundaries

The protected assets are device state, user files, money, externally sent content, irreversible tool outcomes, and the integrity of recorded evidence. The model is not a security boundary. Accessibility text, descriptions, file names, file contents, and tool outputs are attacker-controlled even when they appear inside a structured message.

## Injection paths and defenses

| Path | First defense | Second defense | Residual failure mode |
|---|---|---|---|
| Accessibility `text` / `desc` | `tool:screen` provenance is attached to observations and propagated through later model outputs | `⟦data⟧...⟦/data⟧` wrapping plus a fixed system instruction | A buggy driver that forgets provenance, or a future tool that strips metadata before policy evaluation |
| File names and file contents | `tool:file` provenance is recursively propagated through nested values | Data wrapping when content enters prompts | A parser that converts tagged values to plain strings without calling `provenance_of` can lose the label |
| Model paraphrase of untrusted content | `TaintTracker` unions current untrusted origins with every subsequent model-derived argument | Human approval remains mandatory for irreversible actions | Over-tainting can deny a legitimate action; under-tainting occurs if a model/tool boundary bypasses `Agent` |
| Direct malicious tool call | Capability exact-pair check, effect/risk policy, schema validation | Trace `policy.denied` and trajectory evaluation | A run with excessive grants or an approver that always returns true weakens the policy by configuration |
| Replay against a device | REPLAY rejects every non-`none` tool at registration | Replay adapters only emit recorded results after matching call arguments | A custom executor that bypasses registration and directly talks to a device would violate the runtime contract |
| Crash after irreversible execution | Persistent intent/result pairing enters `unknown`; resume refuses automatic retry | Explicit human `resolve` followed by a new result and `human.intervene` | Human may choose the wrong outcome; the system records but cannot independently prove external state |

## What the prompt layer does not prove

Prompt wrapping is not treated as an injection defense by itself. Models can misunderstand delimiters, follow malicious text anyway, or produce unsafe arguments for unrelated reasons. Authorization and taint checks therefore run after model generation and before every irreversible intent.

## Approval and stubbing

Risky writes are denied by default. Irreversible actions require both their exact capability and a separate approval decision. RECORD may stub the external execution, but only after the same policy checks and an intent record; the result is explicitly marked `stubbed` so it cannot be mistaken for a real side effect.

## Audit limitations

Hash chains and Merkle roots detect later modification; they do not authenticate the original writer or prove that a recorded human decision was correct. Trusted root distribution and operator identity are deployment concerns outside this repository.
