# Diagram Conventions — Mermaid Diagrams for Threat Models & Architecture This file contains ALL rules for creating Mermaid diagrams in threat model reports. It is self-contained — everything needed to produce correct diagrams is here. --- ## ⛔ CRITICAL RULES — READ BEFORE DRAWING ANY DIAGRAM These rules are the most frequently violated. Read them first, and re-check after every diagram. ### Rule 1: Kubernetes Sidecar Co-location (MANDATORY) When the target system runs on Kubernetes, **containers that share a Pod must be represented together** — never as independent standalone components. **DO THIS — annotate the primary container's label:** ``` InferencingFlow(("Inferencing Flow
+ MISE, Dapr")):::process IngestionFlow(("Ingestion Flow
+ MISE, Dapr")):::process VectorDbApi(("VectorDB API
+ Dapr")):::process ``` **DO NOT DO THIS — never create standalone sidecar nodes:** ``` ❌ MISE(("MISE Sidecar")):::process ❌ DaprSidecar(("Dapr Sidecar")):::process ❌ InferencingFlow -->|"localhost"| MISE ``` **Why:** Sidecars (Dapr, MISE/auth proxy, Envoy, Istio proxy, log collectors) share the Pod's network namespace, lifecycle, and security context with their primary container. They are NOT independent services. **This rule applies to ALL diagram types:** architecture, threat model, summary. ### Rule 2: No Intra-Pod Flows (MANDATORY) **DO NOT draw data flows between a primary container and its sidecars.** These are implicit from the co-location annotation. ``` ❌ InferencingFlow -->|"localhost:3500"| DaprSidecar ❌ InferencingFlow -->|"localhost:8080"| MISE ``` Intra-pod communication happens on localhost — it has no security boundary and should not appear in the diagram. ### Rule 3: Cross-Boundary Sidecar Flows Originate from Host Container When a sidecar makes a call that crosses a trust boundary (e.g., MISE → Azure AD, Dapr → Redis), draw the arrow **from the host container node** — never from a standalone sidecar node. ``` ✅ InferencingFlow -->|"HTTPS (MISE auth)"| AzureAD ✅ IngestionAPI -->|"HTTPS (MISE auth)"| AzureAD ✅ InferencingFlow -->|"TCP (Dapr)"| Redis ❌ MISESidecar -->|"HTTPS"| AzureAD ❌ DaprSidecar -->|"TCP"| Redis ``` If multiple pods have the same sidecar calling the same external target, draw one arrow per host container. Multiple arrows to the same target is correct. ### Rule 4: Element Table — No Separate Sidecar Rows Do NOT add separate Element Table rows for sidecars. Describe them in the host container's description column: ``` ✅ | Inferencing Flow | Process | API service + MISE auth proxy + Dapr sidecar | Backend Services | ❌ | MISE Sidecar | Process | Auth proxy for Inferencing Flow | Backend Services | ``` If a sidecar class has its own threat surface (e.g., MISE auth bypass), it gets a `## Component` section in STRIDE analysis — but it is still NOT a separate diagram node. --- ## Pre-Render Checklist (VERIFY BEFORE FINALIZING) After drawing ANY diagram, verify: - [ ] **Every K8s service node annotated with sidecars?** — Each pod's process node includes `
+ SidecarName` for all co-located containers - [ ] **Zero standalone sidecar nodes?** — Search diagram for any node named `MISE`, `Dapr`, `Envoy`, `Istio`, `Sidecar` — these must NOT exist as separate nodes - [ ] **Zero intra-pod localhost flows?** — No arrows between a container and its sidecars on localhost - [ ] **Cross-boundary sidecar flows from host?** — All arrows to external targets (Azure AD, Redis, etc.) originate from the host container node - [ ] **Background forced to white?** — `%%{init}%%` block includes `'background': '#ffffff'` - [ ] **All classDef include `color:#000000`?** — Black text on every element - [ ] **`linkStyle default` present?** — `stroke:#666666,stroke-width:2px` - [ ] **All labels quoted?** — `["Name"]`, `(("Name"))`, `-->|"Label"|` - [ ] **Subgraph/end pairs matched?** — Every `subgraph` has a closing `end` - [ ] **Trust boundary styles applied?** — `stroke:#e31a1c,stroke-width:3px,stroke-dasharray: 5 5` --- ## Color Palette > **⛔ CRITICAL: Use ONLY these exact hex codes. Do NOT invent colors, use Chakra UI colors (#4299E1, #48BB78, #E53E3E), Tailwind colors, or any other palette. The colors below are from ColorBrewer qualitative palettes for colorblind accessibility. COPY the classDef lines VERBATIM from this file.** These colors are shared across ALL Mermaid diagrams. Colors are from ColorBrewer qualitative palettes — designed for colorblind accessibility. | Color Role | Fill | Stroke | Used For | |------------|------|--------|----------| | Blue | `#6baed6` | `#2171b5` | Services/Processes | | Amber | `#fdae61` | `#d94701` | External Interactors | | Green | `#74c476` | `#238b45` | Data Stores | | Red | n/a | `#e31a1c` | Trust boundaries (threat model only) | | Dark gray | n/a | `#666666` | Arrows/links | | Text | all: `color:#000000` | | Black text on every element | ### Design Rationale | Element | Fill | Stroke | Text | Why | |---------|------|--------|------|-----| | Process | `#6baed6` | `#2171b5` | `#000000` | Medium blue — visible on both themes | | External Interactor | `#fdae61` | `#d94701` | `#000000` | Warm amber — distinct from blue/green | | Data Store | `#74c476` | `#238b45` | `#000000` | Medium green — natural for storage | | Trust Boundary | none | `#e31a1c` | n/a | Red dashed — 3px for visibility | | Arrows/Links | n/a | `#666666` | n/a | Dark gray on white background | | Background | `#ffffff` | n/a | n/a | Forced white for dark theme safety | --- ## Forced White Background (REQUIRED) Every Mermaid diagram — flowchart and sequence — MUST include an `%%{init}%%` block that forces a white background. This ensures diagrams render correctly in dark themes. > **⛔ CRITICAL: Do NOT add `primaryColor`, `secondaryColor`, `tertiaryColor`, or ANY custom color keys to themeVariables. The init block controls ONLY the background and line color. ALL element colors come from classDef lines — never from themeVariables. If you add color overrides to themeVariables, they will BREAK the classDef palette.** ### Flowchart Init Block Add as the **first line** of every `.mmd` file or ` ```mermaid ` flowchart: ``` %%{init: {'theme': 'base', 'themeVariables': { 'background': '#ffffff', 'primaryColor': '#ffffff', 'lineColor': '#666666' }}}%% ``` **THE ABOVE IS THE ONLY ALLOWED INIT BLOCK FOR FLOWCHARTS.** Do not modify it. Do not add keys. Copy it verbatim. ### Arrow / Link Default Styling Add after classDef lines: ``` linkStyle default stroke:#666666,stroke-width:2px ``` ### Sequence Diagram Init Block Sequence diagrams cannot use `classDef`. Use this init block: ``` %%{init: {'theme': 'base', 'themeVariables': { 'background': '#ffffff', 'actorBkg': '#6baed6', 'actorBorder': '#2171b5', 'actorTextColor': '#000000', 'signalColor': '#666666', 'signalTextColor': '#666666', 'noteBkgColor': '#fdae61', 'noteBorderColor': '#d94701', 'noteTextColor': '#000000', 'activationBkgColor': '#ddeeff', 'activationBorderColor': '#2171b5', 'sequenceNumberColor': '#767676', 'labelBoxBkgColor': '#f0f0f0', 'labelBoxBorderColor': '#666666', 'labelTextColor': '#000000', 'loopTextColor': '#000000' }}}%% ``` --- ## Diagram Type: Threat Model (DFD) Used in: `1-threatmodel.md`, `1.1-threatmodel.mmd`, `1.2-threatmodel-summary.mmd` ### `.mmd` File Format — CRITICAL The `.mmd` file contains **raw Mermaid source only** — no markdown, no code fences. The file must start on line 1 with: ``` %%{init: {'theme': 'base', 'themeVariables': { 'background': '#ffffff', 'primaryColor': '#ffffff', 'lineColor': '#666666' }}}%% ``` Followed by `flowchart LR` on line 2. NEVER use `flowchart TB`. **WRONG**: File starts with ` ```plaintext ` or ` ```mermaid ` — these are code fences and corrupt the `.mmd` file. ### ClassDef & Shapes ``` classDef process fill:#6baed6,stroke:#2171b5,stroke-width:2px,color:#000000 classDef external fill:#fdae61,stroke:#d94701,stroke-width:2px,color:#000000 classDef datastore fill:#74c476,stroke:#238b45,stroke-width:2px,color:#000000 ``` | Element Type | Shape Syntax | Example | |-------------|-------------|---------| | Process | `(("Name"))` circle | `WebApi(("Web API")):::process` | | External Interactor | `["Name"]` rectangle | `User["User/Browser"]:::external` | | Data Store | `[("Name")]` cylinder | `Database[("PostgreSQL")]:::datastore` | ### Trust Boundary Styling ``` subgraph BoundaryId["Display Name"] %% elements inside end style BoundaryId fill:none,stroke:#e31a1c,stroke-width:3px,stroke-dasharray: 5 5 ``` ### Flow Labels ``` Unidirectional: A -->|"Label"| B Bidirectional: A <-->|"Label"| B ``` ### Data Flow IDs - Detailed flows: `DF01`, `DF02`, `DF03`... - Summary flows: `SDF01`, `SDF02`, `SDF03`... ### Complete DFD Template ```mermaid %%{init: {'theme': 'base', 'themeVariables': { 'background': '#ffffff', 'primaryColor': '#ffffff', 'lineColor': '#666666' }}}%% flowchart LR classDef process fill:#6baed6,stroke:#2171b5,stroke-width:2px,color:#000000 classDef external fill:#fdae61,stroke:#d94701,stroke-width:2px,color:#000000 classDef datastore fill:#74c476,stroke:#238b45,stroke-width:2px,color:#000000 linkStyle default stroke:#666666,stroke-width:2px User["User/Browser"]:::external subgraph Internal["Internal Network"] WebApi(("Web API")):::process Database[("PostgreSQL")]:::datastore end User <-->|"HTTPS"| WebApi WebApi <-->|"SQL/TLS"| Database style Internal fill:none,stroke:#e31a1c,stroke-width:3px,stroke-dasharray: 5 5 ``` ### Kubernetes DFD Template (With Sidecars) ```mermaid %%{init: {'theme': 'base', 'themeVariables': { 'background': '#ffffff', 'primaryColor': '#ffffff', 'lineColor': '#666666' }}}%% flowchart LR classDef process fill:#6baed6,stroke:#2171b5,stroke-width:2px,color:#000000 classDef external fill:#fdae61,stroke:#d94701,stroke-width:2px,color:#000000 classDef datastore fill:#74c476,stroke:#238b45,stroke-width:2px,color:#000000 linkStyle default stroke:#666666,stroke-width:2px User["User/Browser"]:::external IdP["Identity Provider"]:::external subgraph K8s["Kubernetes Cluster"] subgraph Backend["Backend Services"] ApiService(("API Service
+ AuthProxy, Dapr")):::process Worker(("Worker
+ Dapr")):::process end Redis[("Redis")]:::datastore Database[("PostgreSQL")]:::datastore end User -->|"HTTPS"| ApiService ApiService -->|"HTTPS"| User ApiService -->|"HTTPS"| IdP ApiService -->|"SQL/TLS"| Database ApiService -->|"Dapr HTTP"| Worker ApiService -->|"TCP"| Redis Worker -->|"SQL/TLS"| Database style K8s fill:none,stroke:#e31a1c,stroke-width:3px,stroke-dasharray: 5 5 style Backend fill:none,stroke:#e31a1c,stroke-width:3px,stroke-dasharray: 5 5 ``` **Key points:** - AuthProxy and Dapr are annotated on the host node (`+ AuthProxy, Dapr`), not as separate nodes - `ApiService -->|"HTTPS"| IdP` = auth proxy's cross-boundary call, drawn from host container - `ApiService -->|"TCP"| Redis` = Dapr's cross-boundary call, drawn from host container - No intra-pod flows drawn --- ## Diagram Type: Architecture Used in: `0.1-architecture.md` only ### ClassDef & Shapes ``` classDef service fill:#6baed6,stroke:#2171b5,stroke-width:2px,color:#000000 classDef external fill:#fdae61,stroke:#d94701,stroke-width:2px,color:#000000 classDef datastore fill:#74c476,stroke:#238b45,stroke-width:2px,color:#000000 ``` | Element Type | Shape Syntax | Notes | |-------------|-------------|-------| | Services/Processes | `["Name"]` or `(["Name"])` | Rounded rectangles or stadium | | External Actors | `(["Name"])` with `external` class | Amber distinguishes them | | Data Stores | `[("Name")]` cylinder | Same as DFD | | **DO NOT** use circles `(("Name"))` | | Reserved for DFD threat model diagrams | ### Layer Grouping Styling (NOT trust boundaries) ``` style LayerId fill:#f0f4ff,stroke:#2171b5,stroke-width:2px,stroke-dasharray: 5 5 ``` Layer colors: - Backend: `fill:#f0f4ff,stroke:#2171b5` (light blue) - Data: `fill:#f0fff0,stroke:#238b45` (light green) - External: `fill:#fff8f0,stroke:#d94701` (light amber) - Infrastructure: `fill:#f5f5f5,stroke:#666666` (light gray) ### Flow Conventions - Label with **what is communicated**: `"User queries"`, `"Auth tokens"`, `"Log data"` - Protocol can be parenthetical: `"Queries (gRPC)"` - Simpler arrows than DFD — use `-->` without requiring bidirectional flows ### Kubernetes Pods in Architecture Diagrams Show pods with their full container composition: ``` inf["Inferencing Flow
+ MISE + Dapr"]:::service ing["Ingestion Flow
+ MISE + Dapr"]:::service ``` ### Key Difference from DFD The architecture diagram shows **what the system does** (logical components and interactions). The threat model DFD shows **what could be attacked** (trust boundaries, data flows with protocols, element types). They share many components but serve different purposes. ### Complete Architecture Diagram Template ```mermaid %%{init: {'theme': 'base', 'themeVariables': { 'background': '#ffffff', 'primaryColor': '#ffffff', 'lineColor': '#666666' }}}%% flowchart LR classDef service fill:#6baed6,stroke:#2171b5,stroke-width:2px,color:#000000 classDef external fill:#fdae61,stroke:#d94701,stroke-width:2px,color:#000000 classDef datastore fill:#74c476,stroke:#238b45,stroke-width:2px,color:#000000 linkStyle default stroke:#666666,stroke-width:2px User(["User"]):::external subgraph Backend["Backend Services"] Api["API Service"]:::service Worker["Worker"]:::service end subgraph Data["Data Layer"] Db[("Database")]:::datastore Cache[("Cache")]:::datastore end User -->|"HTTPS"| Api Api --> Worker Worker --> Db Api --> Cache style Backend fill:#f0f4ff,stroke:#2171b5,stroke-width:2px,stroke-dasharray: 5 5 style Data fill:#f0fff0,stroke:#238b45,stroke-width:2px,stroke-dasharray: 5 5 ``` ### Kubernetes Architecture Template ```mermaid %%{init: {'theme': 'base', 'themeVariables': { 'background': '#ffffff', 'primaryColor': '#ffffff', 'lineColor': '#666666' }}}%% flowchart LR classDef service fill:#6baed6,stroke:#2171b5,stroke-width:2px,color:#000000 classDef external fill:#fdae61,stroke:#d94701,stroke-width:2px,color:#000000 classDef datastore fill:#74c476,stroke:#238b45,stroke-width:2px,color:#000000 linkStyle default stroke:#666666,stroke-width:2px User(["User"]):::external IdP(["Azure AD"]):::external subgraph K8s["Kubernetes Cluster"] Inf["Inferencing Flow
+ MISE + Dapr"]:::service Ing["Ingestion Flow
+ MISE + Dapr"]:::service Redis[("Redis")]:::datastore end User -->|"HTTPS"| Inf Inf -->|"Auth (MISE)"| IdP Ing -->|"Auth (MISE)"| IdP Inf -->|"State (Dapr)"| Redis style K8s fill:#f0f4ff,stroke:#2171b5,stroke-width:2px,stroke-dasharray: 5 5 ``` --- ## Sequence Diagram Rules Used in: `0.1-architecture.md` top scenarios - The **first 3 scenarios MUST** each include a Mermaid `sequenceDiagram` - Scenarios 4-5 may optionally include one - Use the **Sequence Diagram Init Block** above at the top of each - Use `participant` aliases matching the Key Components table - Show activations (`activate`/`deactivate`) for request-response patterns - Include `Note` blocks for security-relevant steps (e.g., "Validates JWT token") - Keep diagrams focused — core workflow, not every error path ### Complete Sequence Diagram Example ```mermaid %%{init: {'theme': 'base', 'themeVariables': { 'background': '#ffffff', 'actorBkg': '#6baed6', 'actorBorder': '#2171b5', 'actorTextColor': '#000000', 'signalColor': '#666666', 'signalTextColor': '#666666', 'noteBkgColor': '#fdae61', 'noteBorderColor': '#d94701', 'noteTextColor': '#000000', 'activationBkgColor': '#ddeeff', 'activationBorderColor': '#2171b5' }}}%% sequenceDiagram actor User participant Api as API Service participant Db as Database User->>Api: POST /resource activate Api Note over Api: Validates JWT token Api->>Db: INSERT query Db-->>Api: Result Api-->>User: 201 Created deactivate Api ``` --- ## Summary Diagram Rules Used in: `1.2-threatmodel-summary.mmd` (generated only when detailed diagram has >15 elements or >4 trust boundaries) 1. **All trust boundaries must be preserved** — never combine or omit 2. **Only combine components that are NOT**: entry points, core flow components, security-critical services, primary data stores 3. **Candidates for aggregation**: supporting infrastructure, secondary caches, multiple externals at same trust level 4. **Combined element labels must list contents:** ``` DataLayer[("Data Layer
(UserDB, OrderDB, Redis)")] SupportServices(("Supporting
(Logging, Monitoring)")) ``` 5. Use `SDF` prefix for summary data flows: `SDF01`, `SDF02`, ... 6. Include mapping table in `1-threatmodel.md`: ``` | Summary Element | Contains | Summary Flows | Maps to Detailed Flows | ``` --- ## Naming Conventions | Item | Convention | Example | |------|-----------|---------| | Element ID | PascalCase, no spaces | `WebApi`, `UserDb` | | Display Name | Human readable in quotes | `"Web API"`, `"User Database"` | | Flow Label | Protocol or action in quotes | `"HTTPS"`, `"SQL"`, `"gRPC"` | | Flow ID | Unique short identifier | `DF01`, `DF02` | | Boundary ID | PascalCase | `InternalNetwork`, `PublicDMZ` | **CRITICAL: Always quote ALL text in Mermaid diagrams:** - Element labels: `["Name"]`, `(("Name"))`, `[("Name")]` - Flow labels: `-->|"Label"|` - Subgraph titles: `subgraph ID["Title"]` --- ## Quick Reference - Shapes ``` External Interactor: ["Name"] → Rectangle Process: (("Name")) → Circle (double parentheses) Data Store: [("Name")] → Cylinder ``` ## Quick Reference - Flows ``` Unidirectional: A -->|"Label"| B Bidirectional: A <-->|"Label"| B ``` ## Quick Reference - Boundaries ``` subgraph BoundaryId["Display Name"] %% elements inside end style BoundaryId fill:none,stroke:#e31a1c,stroke-width:3px,stroke-dasharray: 5 5 ``` --- ## STRIDE Analysis — Sidecar Implications Although sidecars are NOT separate diagram nodes, they DO appear in STRIDE analysis: - Sidecars with distinct threat surfaces (e.g., MISE auth bypass, Dapr mTLS) get their own `## Component` section in `2-stride-analysis.md` - The component heading notes which pods they are co-located in - Threats related to intra-pod communication (localhost bypass, shared namespace) go under the **primary container's** component section - **Pod Co-location** line in STRIDE template: list co-located sidecars (e.g., "MISE Sidecar, Dapr Sidecar")