Architecture diagrams are usually drawn to explain how a system works. That is useful, but it is not the question a security review asks. A review asks where trust changes: where a request from someone you do not control reaches something you care about.
The good news is that you can answer that question on a single page. The diagram does not need to be pretty or complete. It needs to show the boundaries, and every arrow that crosses them.
What counts as a trust boundary
A trust boundary is any point where data or control passes between two parties that hold different privileges or are controlled by different people. Some are obvious, such as the internet edge. Many are not:
- Between the browser and your API, because the browser belongs to the user.
- Between tenants inside the same database, because one customer must not read another.
- Between your service and a third-party webhook, because the sender is someone else.
- Between the CI runner and production, because the runner executes code from pull requests.
- Between a support tool and customer data, because staff access is a privilege in its own right.
- Between an AI agent and the tools it can call, because the prompt may come from untrusted content.
If you only draw the first one, the review will only find problems at the first one.
Five kinds of box
Keep the notation small. We use five shapes, which line up roughly with the elements of a classic data flow diagram:
| Shape | Means | Example |
|---|---|---|
| Person | A human actor | Customer, admin, support agent |
| Box | Something you run | API, worker, admin panel |
| Cylinder | Something that stores data | Database, bucket, queue |
| Cloud | Something you do not run | Payment provider, identity provider, SaaS API |
| Dashed line | A trust boundary | Internet edge, tenant boundary, CI to prod |
Arrows show data flow and point in the direction the request travels. Label each arrow with the protocol and what it carries: HTTPS, JSON, invoice ids. That label is what turns the drawing into something you can review.
Anatomy of a missed boundary
Most review findings follow the same shape. A boundary exists, nobody drew it, so nobody asked the questions that go with it. A common example is an internal admin panel that was assumed to be safe because it sits "inside":
- AssumptionThe admin panel is internal, so it skips per-object authorisation checks.
- ExposureIt is reachable from the corporate network or a VPN that many people and devices share.
- FootholdOne staff laptop or one shared credential is compromised.
- ImpactThe attacker reads or edits any customer record through a tool that trusted its network location.
Drawing a dashed line between "staff" and "admin panel" forces the question the design skipped: how does this service know who is calling, and what they are allowed to touch?
The questions for every crossing arrow
Once the boundaries are drawn, go arrow by arrow. For every arrow that crosses a dashed line, write short answers to these questions. STRIDE is a handy prompt list here:
- Spoofing. How is the caller authenticated? Token, mTLS, signature, nothing?
- Tampering. Is the input validated on the receiving side, not just the sending side?
- Repudiation. Is the call logged with who made it?
- Information disclosure. Does the response contain more than the caller should see?
- Denial of service. Is there a limit on rate, size or cost?
- Elevation of privilege. Can the caller reach actions above their role?
Blank answers are your findings list. In practice the empty cells cluster on two or three arrows, and those are where review time should go.
Keeping the page current
A diagram drawn once for a review goes stale within a quarter. Two habits keep it honest:
- Store it as code. A text format such as Mermaid or PlantUML in the repository means changes to it show up in pull requests next to the code that caused them.
- Tie it to change. A new external integration, a new data store or a new role is a reason to update the page. Put that in the pull request template.
A small Mermaid sketch is enough to start:
flowchart LR
user([Customer]) -->|HTTPS, JSON| api[API]
subgraph tenant [Tenant boundary]
api -->|SQL, row-level filter| db[(Postgres)]
end
psp((Payment provider)) -->|Signed webhook| api
ci[CI runner] -.->|Deploy token| api
Check yours in 10 minutes
- Open your current architecture diagram. Count the dashed lines. If there are none, start there.
- Add a boundary for staff tools, CI to production and every third-party callback.
- Pick the three arrows that cross the most sensitive boundary and answer the six questions.
- Any blank answer becomes a ticket, or a topic for a proper architecture review.
Where this sits on the line
In the tower (in development):