Threat models that live next to the code
A threat model that lives in a drawing tool is out of date by the first sprint review. Security teams keep data-flow diagrams with trust boundaries in the repo — docs-as-code — and increasingly have an AI draft the first pass from the code itself. mdd gives that draft a structured JSON model and cluster boxes for the boundaries that matter.
A data flow with a trust boundary
A request path crossing into a trusted zone, as spliced into a threat-model doc. The cluster box is the trust boundary; the JSON is the model your agent drafts and reviewers correct.
<!-- mdd:begin id=dataflow src=docs/threat-model.diagram.json -->
```
┌──────────────┐
│ User Browser │
└───────┬──────┘
TLS
│
┌──────▼──────┐
│ API Gateway │
└──────┬──────┘
│
│
┌┄ trust boundary: VPC ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┐
┆ ┌──────▼──────┐ ┌──────────────┐ ┆
┆ │ App Service │ │ Async Worker │ ┆
┆ └──────┬──────┘ └───────┬──────┘ ┆
└┄┄┄┄┄┄┄┄┼┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┼┄┄┄┄┄┄┄┄┘
┌───┼┬───────────┐ │
├───┘└───────────┼─────┘
┌─────▼────┐ ┌───────▼───────┐
│ Postgres │ │ Secrets Store │
└──────────┘ └───────────────┘
```
[diagram source](docs/threat-model.diagram.json)
<!-- mdd:end -->The model your agent edits
{
"version": 1,
"boxes": [
{
"id": "browser",
"label": "User Browser",
"row": 0,
"connections": [
{
"to": "gateway",
"arrow": "to",
"label": "TLS"
}
]
},
{
"id": "gateway",
"label": "API Gateway",
"row": 1,
"connections": [
{
"to": "api",
"arrow": "to"
}
]
},
{
"id": "api",
"label": "App Service",
"row": 2,
"connections": [
{
"to": "db",
"arrow": "to"
},
{
"to": "secrets",
"arrow": "to"
}
]
},
{
"id": "worker",
"label": "Async Worker",
"row": 2,
"connections": [
{
"to": "db",
"arrow": "to"
}
]
},
{
"id": "db",
"label": "Postgres",
"row": 3
},
{
"id": "secrets",
"label": "Secrets Store",
"row": 3
}
],
"clusters": [
{
"id": "trusted",
"label": "trust boundary: VPC",
"members": [
"api",
"worker"
]
}
]
}Your agent draws it, you review it
- Have your agent draft the data-flow model from the code: entry points, services, stores, and which of them sit inside the trust boundary.
- Splice it into docs/threat-model.md with mdd render.
- Review the draft like code — move boxes across the boundary, add the flows the agent missed, re-render.
- When the architecture changes, the model changes with it; mdd check keeps the diagram honest.
A prompt to hand your agent
Draft and maintain the data-flow diagram in docs/threat-model.md. Model entry points, services, and data stores in docs/threat-model.diagram.json, using a cluster for the trust boundary, then run: npx mdd render --json docs/threat-model.diagram.json --file docs/threat-model.md --id dataflow.
Why mdd here
Threat modeling is moving into the repo because that is where it stays current, and "AI drafts the threat model from the code" only works if the draft is a structured artifact a human can correct. mdd's JSON model is that artifact: cluster boxes mark trust boundaries, validation catches broken references, and the ASCII renders in the security review diff.
More use cases
- AGENTS.md architecture maps — An architecture map in your agent context file: ASCII for humans, JSON ground truth for the agent.
- Diagrams in code comments — Header-comment state machines and layouts, computed from JSON and spliced behind // or #.
- Runbook flowcharts — Decision flows and failure maps readable in any terminal, drafted and updated by your incident tooling.
- Design docs, RFCs & ADRs — RFC and ADR system diagrams that stay legible in diffs across every revision round.
- ASCII sequence diagrams — Auth flows and call chains as plain text: message order in the JSON is the layout.
- Data pipeline lineage maps — A curated lineage map your agent keeps current as pipeline models change.
- Multi-agent orchestration maps — Agent hand-offs, tool wiring, and memory flow — documentation the agents themselves can update.
- Tutorials & conceptual docs — Conceptual diagrams that survive every downstream medium: docs site, README, PDF, chat.
- diagram spec (clusters & boundaries)