# Choreo
[](https://hex.pm/packages/choreo)
[](https://hexdocs.pm/choreo/)
[](https://github.com/code-shoily/choreo/actions)
[](https://coveralls.io/github/code-shoily/choreo?branch=main)
[](https://opensource.org/licenses/MIT)
> Domain-specific diagram builders and graph analyzers on top of [Yog](https://github.com/code-shoily/yog_ex).
Choreo is a family of Elixir libraries that let you model, analyze, and render complex systems as graphs. Instead of drawing boxes and arrows by hand, you write code. Instead of static pictures, you get live analysis — reachability, cycles, bottlenecks, threat generation, and more.
```elixir
alias Choreo.Dataflow
# A dataflow pipeline with one line of analysis
pipeline =
Dataflow.new()
|> Dataflow.add_source(:sensor)
|> Dataflow.add_transform(:parse)
|> Dataflow.add_sink(:db)
|> Dataflow.connect(:sensor, :parse)
|> Dataflow.connect(:parse, :db)
Dataflow.Analysis.cyclic?(pipeline) #=> false
Dataflow.to_dot(pipeline) #=> DOT string
Dataflow.to_mermaid(pipeline) #=> Mermaid.js string
```
---
## Table of Contents
- [Why Choreo?](#why-choreo)
- [Installation](#installation)
- [Graph Analysis & Heatmaps](#graph-analysis--heatmaps)
- [Modules](#modules)
- [Choreo — Infrastructure Architecture](#choreo--infrastructure-architecture)
- [Choreo.C4 — C4 Model Architecture](#choreoc4--c4-model-architecture)
- [Choreo.FSM — Finite State Machines](#choreofsm--finite-state-machines)
- [Choreo.Dataflow — Pipeline Diagrams](#choreodataflow--pipeline-diagrams)
- [Choreo.Dependency — Software Dependency Graphs](#choreodependency--software-dependency-graphs)
- [Choreo.DecisionTree — Classification Trees](#choreodecisiontree--classification-trees)
- [Choreo.MindMap — Concept Mapping](#choreomindmap--concept-mapping)
- [Choreo.Planner — Project Planning](#choreoplanner--project-planning)
- [Choreo.Sequence — Sequence Diagrams](#choreosequence--sequence-diagrams)
- [Choreo.ThreatModel — STRIDE Threat Modeling](#choreothreatmodel--stride-threat-modeling)
- [Choreo.Workflow — Task Orchestration](#choreoworkflow--task-orchestration)
- [Choreo.ERD — Database Entity-Relationship Modeling](#choreoerd--database-entity-relationship-modeling)
- [Choreo.UML — Class & Struct Diagrams](#choreouml--class--struct-diagrams)
- [Themes & Rendering](#themes--rendering)
- [Testing](#testing)
- [Roadmap](#roadmap)
---
## Why Choreo?
Most diagramming tools are **visualization-only**: you describe a picture, you get a picture.
Choreo is **analysis-first**: you describe a system, you get answers.
| Question | Choreo answer |
|----------|-----------------|
| "Can this state machine accept input `X`?" | `Choreo.FSM.Analysis.accepts?(fsm, ["X"])` |
| "Is my pipeline cyclic?" | `Choreo.Dataflow.Analysis.cyclic?(pipeline)` |
| "What breaks if I change `:auth`?" | `Choreo.Dependency.Analysis.affected_by(deps, :auth)` |
| "What's the slowest path end-to-end?" | `Choreo.Dataflow.Analysis.longest_path(pipeline)` |
| "Are there circular dependencies?" | `Choreo.Dependency.Analysis.cyclic_dependencies(deps)` |
| "What threats exist in my architecture?" | `Choreo.ThreatModel.Analysis.stride_threats(model)` |
| "Which feature drives the most splits?" | `Choreo.DecisionTree.Analysis.feature_importance(tree)` |
| "How deep is this mind map?" | `Choreo.MindMap.Analysis.depth(map)` |
| "Which ideas are orphaned?" | `Choreo.MindMap.Analysis.orphan_nodes(map)` |
| "Which tasks are ready to work on?" | `Choreo.Planner.Analysis.ready(project)` |
| "What's blocking this milestone?" | `Choreo.Planner.Analysis.blocked(project)` |
| "What is the shortest join path from A to B?" | `Choreo.ERD.Analysis.shortest_join_path(erd, :a, :b)` |
| "Does this struct satisfy its behavior contract?" | `Choreo.UML.Analysis.broken_contracts(uml)` |
| "What does our architecture look like at different zoom levels?" | `Choreo.C4.to_mermaid(Choreo.View.zoom(c4, level: 2))` |
| "What order do these service calls happen in?" | `Choreo.Sequence.messages(seq)` |
| "What's the shortest path from A to B?" | `Choreo.View.focus_between(map, :a, :b)` |
| "Collapse these nodes into one?" | `Choreo.View.collapse(map, pred, :agg)` |
Everything renders to **DOT (Graphviz)** for publication-quality output and **Mermaid.js** for native rendering in GitHub, GitLab, Notion, and Livebook — with built-in `:default`, `:dark`, `:warm`, `:forest`, `:ocean`, and custom themes.
---
## Graph Analysis & Heatmaps
Choreo provides powerful graph analysis tools to identify "hotspots" in your architecture, workflows, and pipelines. Use `heatmap/2` to automatically color nodes based on importance or performance metrics.
| Metric | Measure | Question | Best for |
|--------|---------|----------|----------|
| **Structural Importance** | Betweenness Centrality | "Which nodes are critical bridges/connectors?" | `Choreo`, `Dependency` |
| **Connectivity** | Degree Centrality | "Which nodes have the most connections?" | `MindMap`, `Dependency` |
| **SPOF Detection** | Articulation Points | "Which nodes would disconnect the system if they failed?" | `Choreo`, `Dataflow` |
| **Nucleus Detection** | K-Core Decomposition | "Which nodes form the most tightly-coupled core?" | `Choreo`, `Dependency` |
| **Dependency Reduction** | Transitive Reduction | "What is the minimal set of dependencies that preserve reachability?" | `Dependency` |
| **Path Analysis** | Dijkstra / Widest Path | "What is the fastest or highest-throughput path between two points?" | `Workflow`, `Dataflow` |
| **Execution Hotspots** | Latency Heatmap | "Which tasks slow down the entire workflow?" | `Workflow` |
| **Volume Hotspots** | Throughput Heatmap | "Which stages handle the most data volume?" | `Dataflow` |
| **Security Hotspots** | Risk Heatmap | "Which components have the most security threats?" | `ThreatModel` |
```elixir
# Example: Visualizing security risk hotspots
model = Choreo.ThreatModel.Analysis.heatmap(model, palette: :heat)
Choreo.ThreatModel.to_dot(model)
# Example: Transitive Reduction for dependency cleanup
{:ok, reduced} = Choreo.Analysis.reduce_transitive(deps)
# Example: Finding and highlighting the fastest path
{:ok, path} = Choreo.Analysis.path(wf, :start, :done, measure: :latency)
Choreo.Workflow.to_dot(wf, Choreo.Analysis.highlight(path))
# Example: Custom weighted pathfinding (e.g. by 'cost')
{:ok, path} = Choreo.Analysis.path(system, :a, :b, measure: :cost)
dot = Choreo.Workflow.to_dot(wf, Choreo.Analysis.highlight(path))
```
---
## Installation
Add `choreo` to your `mix.exs`:
```elixir
def deps do
[
{:choreo, "~> 0.8"}
]
end
```
---
## Modules
### Choreo — Infrastructure Architecture
Model systems with typed infrastructure nodes: databases, caches, services, queues, load balancers, networks, users, and storage.
```elixir
alias Choreo
system =
Choreo.new()
|> Choreo.add_database(:db, name: "Postgres", kind: :postgres)
|> Choreo.add_cache(:cache, name: "Redis")
|> Choreo.add_service(:api, name: "API Gateway")
|> Choreo.connect(:api, :cache, cost: 5)
|> Choreo.connect(:api, :db, cost: 10)
# Analysis
{:ok, mst} = Choreo.Analysis.mst(system)
{:ok, order} = Choreo.Analysis.topological_sort(system)
# Render
dot = Choreo.to_dot(system, theme: :dark)
mermaid = Choreo.to_mermaid(system)
```
**Features:** clusters with nesting, dataflow edges, cost-weighted edges, MST, topological sort, SCC, theming.
```mermaid
graph TD
classDef default color:white
cache{{"Redis"}}
db[("Postgres")]
api[["API Gateway"]]
style cache fill:#f59e0b,stroke:#d78000
style db fill:#3b82f6,stroke:#1d64d8
style api fill:#10b981,stroke:#009b63
api --> cache
api --> db
linkStyle 0 stroke-width:2px,stroke:#64748b
linkStyle 1 stroke-width:2px,stroke:#64748b
```
---
### Choreo.C4 — C4 Model Architecture
Model software architecture at multiple zoom levels — from system context down to components — in a single unified graph.
```elixir
alias Choreo.C4
alias Choreo.C4.Analysis
c4 =
C4.new()
|> C4.add_person(:customer, label: "Customer")
|> C4.add_software_system(:banking,
label: "Internet Banking",
scope: :in,
description: "Allows customers to view balances and make payments"
)
|> C4.add_software_system(:mainframe,
label: "Mainframe Banking",
description: "Stores all core banking information"
)
|> C4.add_container(:web_app,
label: "Web Application",
technology: "JavaScript",
parent: :banking
)
|> C4.add_container(:api,
label: "API Application",
technology: "Elixir/Phoenix",
parent: :banking
)
|> C4.add_component(:accounts_ctx,
label: "Accounts Context",
technology: "Elixir",
parent: :api
)
|> C4.relationship(:customer, :banking, label: "Views account balances")
|> C4.relationship(:banking, :mainframe, label: "Gets account info")
# Zoom to a specific C4 level
C4.to_mermaid(Choreo.View.zoom(c4, level: 0)) # System Context
C4.to_mermaid(Choreo.View.zoom(c4, level: 1)) # Container diagram
C4.to_mermaid(Choreo.View.zoom(c4, level: 2)) # Component diagram
# Analysis
Analysis.missing_descriptions(c4)
Analysis.isolated_nodes(c4)
Analysis.validate(c4)
```
**Features:** L1–L3 C4 modeling (System Context, Containers, Components), zoom-aware rendering, parent/child clustering, missing metadata detection, isolated node detection.
```mermaid
graph TD
classDef default color:white
banking["Internet Banking"]
customer(("Customer"))
mainframe["Mainframe Banking"]
style banking fill:#3b82f6,stroke:#1d64d8,stroke-width:3px
style customer fill:#f59e0b,stroke:#d78000
style mainframe fill:#64748b,stroke:#46566d
banking --> mainframe
customer --> banking
linkStyle 0 stroke-width:2px,stroke:#64748b
linkStyle 1 stroke-width:2px,stroke:#64748b
```
---
### Choreo.FSM — Finite State Machines
Classic state machines with initial states, final states, and labeled transitions.
```elixir
alias Choreo.FSM
fsm =
FSM.new()
|> FSM.add_initial_state(:idle)
|> FSM.add_state(:running)
|> FSM.add_final_state(:done)
|> FSM.add_transition(:idle, :running, label: "start")
|> FSM.add_transition(:running, :done, label: "finish")
# Analysis
FSM.Analysis.accepts?(fsm, ["start", "finish"]) #=> true
FSM.Analysis.deterministic?(fsm) #=> true
FSM.Analysis.shortest_accepting_path(fsm) #=> {:ok, ["start", "finish"]}
# Render to native state diagram
FSM.to_mermaid(fsm, syntax: :state_diagram)
```
**Features:** Deterministic execution, reachability, dead-state detection, determinism check, complement, product construction, equivalence checking.
```mermaid
stateDiagram-v2
[*] --> idle
idle --> running : start
running --> done : finish
done --> [*]
```
---
### Choreo.Dataflow — Pipeline Diagrams
Model stream-processing and ETL pipelines. Nodes are sources, transforms, buffers, conditionals, merges, and sinks.
```elixir
alias Choreo.Dataflow
pipeline =
Dataflow.new()
|> Dataflow.add_source(:sensor, label: "IoT Sensor", rate: 1000)
|> Dataflow.add_transform(:parse, label: "JSON Parser", latency_ms: 50)
|> Dataflow.add_buffer(:kafka, label: "Events", capacity: 10_000)
|> Dataflow.add_sink(:db, label: "TimescaleDB")
|> Dataflow.connect(:sensor, :parse, data_type: "raw bytes")
|> Dataflow.connect(:parse, :kafka, data_type: "event")
|> Dataflow.connect(:kafka, :db, data_type: "metrics")
# Analysis
Dataflow.Analysis.cyclic?(pipeline) #=> false
{:ok, order} = Dataflow.Analysis.topological_sort(pipeline)
Dataflow.Analysis.orphan_nodes(pipeline) #=> []
Dataflow.Analysis.bottlenecks(pipeline) #=> [:kafka]
Dataflow.Analysis.simulate(pipeline) #=> throughput map
{:ok, path, latency} = Dataflow.Analysis.longest_path(pipeline)
```
**Features:** error/retry/dead-letter path types, sub-pipeline clusters, throughput simulation, backpressure detection, critical-path analysis.
```mermaid
graph TD
classDef default color:white
parse[["JSON Parser"]]
db["TimescaleDB"]
sensor(["IoT Sensor<br/>1000 evt/s"])
kafka[("Events<br/>(cap: 10000)")]
style parse fill:#3b82f6
style db fill:#f43f5e
style sensor fill:#10b981
style kafka fill:#f59e0b
parse -->|event| kafka
sensor -->|raw bytes| parse
kafka -->|metrics| db
linkStyle 0 stroke-width:2px,stroke:#64748b
linkStyle 1 stroke-width:2px,stroke:#64748b
linkStyle 2 stroke-width:2px,stroke:#64748b
```
---
### Choreo.Dependency — Software Dependency Graphs
Map modules, libraries, applications, interfaces, and tests. Detect circular dependencies, layering violations, and impact zones.
```elixir
alias Choreo.Dependency
deps =
Dependency.new()
|> Dependency.add_application(:api, label: "API Gateway")
|> Dependency.add_module(:auth, label: "Auth")
|> Dependency.add_library(:phoenix)
|> Dependency.depends_on(:api, :auth, type: :calls)
|> Dependency.depends_on(:auth, :phoenix, type: :uses)
# Analysis
Dependency.Analysis.cyclic_dependencies(deps) #=> []
Dependency.Analysis.affected_by(deps, :auth) #=> [:api]
Dependency.Analysis.depends_on(deps, :api) #=> [:auth, :phoenix]
# Render to native class diagram
Dependency.to_mermaid(deps, syntax: :class_diagram)
```
**Features:** cycle path extraction (not just boolean), transitive impact analysis, layer violation detection, centrality ranking, longest dependency chain, cycle edge highlighting in DOT.
```mermaid
classDiagram
direction TD
class api["API Gateway"] {
<<application>>
}
class auth["Auth"] {
<<module>>
}
class phoenix["phoenix"] {
<<library>>
}
api ..> auth : calls
auth --> phoenix : uses
```
---
### Choreo.UML — Class & Struct Diagrams
Model software module contracts, behaviors, protocols, structs, their fields, arities, and visibilities, and declare exact structural relations with visual parity.
```elixir
alias Choreo.UML
uml =
UML.new()
|> UML.add_class(:user,
type: :struct,
label: "User Model",
fields: [
%{name: :id, type: :integer, visibility: :public},
%{name: :email, type: :string, visibility: :private}
],
functions: [
%{name: "authenticate", arity: 2, return: :boolean, visibility: :public}
]
)
|> UML.add_class(:auth_provider, type: :behavior)
|> UML.add_relationship(:user, :auth_provider, type: :realizes, label: "implements")
# Render to native Mermaid class diagram
UML.to_mermaid(uml, syntax: :class_diagram)
```
**Features:** multi-compartment record tables, standard visibility markers (`+`, `-`, `#`), functional contracts, protocol realization, behavior adoption, lens focusing via `Choreo.View`.
```mermaid
classDiagram
direction TD
class user["User Model"] {
<<struct>>
+id integer
-email string
+authenticate(2) boolean
}
class auth_provider["auth_provider"] {
<<behavior>>
}
user ..|> auth_provider : implements
```
---
### Choreo.DecisionTree — Classification Trees
Build decision trees with enforced tree invariants (single root, single parent, no cycles).
```elixir
alias Choreo.DecisionTree
alias Choreo.DecisionTree.Analysis
tree =
DecisionTree.new()
|> DecisionTree.set_root(:weather, feature: "weather")
|> DecisionTree.add_decision(:wind, feature: "wind")
|> DecisionTree.add_outcome(:play, label: "Play", class: "yes")
|> DecisionTree.add_outcome(:stay, label: "Stay", class: "no")
|> DecisionTree.branch(:weather, :wind, "cloudy")
|> DecisionTree.branch(:weather, :play, "sunny")
|> DecisionTree.branch(:wind, :stay, "stormy")
# Evaluation
Analysis.decide(tree, %{"weather" => "cloudy", "wind" => "calm"})
#=> {:ok, [:weather, :wind, ...], "..."}
# Metrics
Analysis.paths(tree) #=> all root-to-leaf paths
Analysis.depth(tree) #=> 2
Analysis.feature_importance(tree) #=> %{"weather" => 1, "wind" => 1}
# Optimization
pruned = Analysis.prune_redundant(tree)
```
**Features:** exact-match decision evaluation, path enumeration with conditions, redundant-branch pruning, feature-importance counting, tree validation.
```mermaid
graph TD
classDef default color:white
weather{"weather"}
wind{"wind"}
play["Play"]
stay["Stay"]
style weather fill:#8b5cf6,stroke:#6d3ed8,stroke-width:3px
style wind fill:#3b82f6,stroke:#1d64d8
style play fill:#10b981,stroke:#009b63
style stay fill:#10b981,stroke:#009b63
weather -->|cloudy| wind
weather -->|sunny| play
wind -->|stormy| stay
linkStyle 0 stroke-width:2px,stroke:#64748b
linkStyle 1 stroke-width:2px,stroke:#64748b
linkStyle 2 stroke-width:2px,stroke:#64748b
```
---
### Choreo.Workflow — Task Orchestration
Model automated task orchestration with Saga-pattern compensations, timeouts, retries, and conditional branching.
```elixir
alias Choreo.Workflow
alias Choreo.Workflow.Analysis
workflow =
Workflow.new()
|> Workflow.add_start(:order_received)
|> Workflow.add_task(:charge_card, timeout_ms: 5000, retry: 3)
|> Workflow.add_task(:reserve_inventory, timeout_ms: 3000)
|> Workflow.add_decision(:sufficient_stock)
|> Workflow.add_task(:pack_items, timeout_ms: 10_000)
|> Workflow.add_task(:ship_order, timeout_ms: 5000)
|> Workflow.add_compensation(:refund_payment, for: :charge_card)
|> Workflow.add_end(:done)
|> Workflow.connect(:order_received, :charge_card)
|> Workflow.connect(:charge_card, :reserve_inventory)
|> Workflow.connect(:reserve_inventory, :sufficient_stock)
|> Workflow.connect(:sufficient_stock, :pack_items, condition: "yes")
|> Workflow.connect(:sufficient_stock, :refund_payment, condition: "no", edge_type: :compensation)
|> Workflow.connect(:pack_items, :ship_order)
|> Workflow.connect(:ship_order, :done)
# Analysis
Analysis.critical_path(workflow)
#=> {:ok, [:order_received, :charge_card, ...], 23000}
Analysis.parallelizable_tasks(workflow)
Analysis.missing_compensations(workflow)
Analysis.validate(workflow)
```
**Features:** critical-path analysis with latency weights, parallelizable-task grouping, failure-scenario detection, missing-compensation detection, bottleneck detection, execution simulation.
```mermaid
graph TD
classDef default color:white
done(("done"))
order_received(("order_received"))
charge_card[["charge_card (5000ms) retry: 3"]]
reserve_inventory[["reserve_inventory (3000ms)"]]
sufficient_stock{"sufficient_stock"}
pack_items[["pack_items (10000ms)"]]
ship_order[["ship_order (5000ms)"]]
refund_payment["refund_payment"]
style done fill:#ef4444,stroke:#d12626,stroke-width:3px
style order_received fill:#10b981,stroke:#009b63,stroke-width:2px
style charge_card fill:#3b82f6,stroke:#1d64d8
style reserve_inventory fill:#3b82f6,stroke:#1d64d8
style sufficient_stock fill:#8b5cf6,stroke:#6d3ed8
style pack_items fill:#3b82f6,stroke:#1d64d8
style ship_order fill:#3b82f6,stroke:#1d64d8
style refund_payment fill:#f87171,stroke:#ef4444,stroke-width:2px,stroke-dasharray:3 3
order_received --> charge_card
charge_card --> reserve_inventory
reserve_inventory --> sufficient_stock
sufficient_stock -->|yes| pack_items
sufficient_stock -->|no| refund_payment
pack_items --> ship_order
ship_order --> done
linkStyle 0 stroke-width:2px,stroke:#64748b
linkStyle 1 stroke-width:2px,stroke:#64748b
linkStyle 2 stroke-width:2px,stroke:#64748b
linkStyle 3 stroke-width:2px,stroke:#64748b
linkStyle 4 stroke-width:2px,stroke:#64748b,stroke-dasharray:5 5
linkStyle 5 stroke-width:2px,stroke:#64748b
linkStyle 6 stroke-width:2px,stroke:#64748b
```
---
### Choreo.MindMap — Concept Mapping
Model hierarchical concept maps with a central root, branching topics and subtopics, and associative cross-links.
```elixir
alias Choreo.MindMap
alias Choreo.MindMap.Analysis
map =
MindMap.new()
|> MindMap.set_root(:elixir, label: "Elixir")
|> MindMap.add_topic(:concurrency, label: "Concurrency")
|> MindMap.add_topic(:ecosystem, label: "Ecosystem")
|> MindMap.add_subtopic(:processes, label: "Processes")
|> MindMap.add_note(:beam, label: "BEAM VM")
|> MindMap.branch(:elixir, :concurrency)
|> MindMap.branch(:elixir, :ecosystem)
|> MindMap.branch(:concurrency, :processes)
|> MindMap.branch(:ecosystem, :beam)
|> MindMap.associate(:processes, :beam, label: "runs on")
# Analysis
Analysis.depth(map) #=> 2
Analysis.breadth(map) #=> 2
Analysis.orphan_nodes(map) #=> []
Analysis.paths(map) #=> [[:elixir, :concurrency, :processes], [:elixir, :ecosystem, :beam]]
Analysis.validate(map) #=> []
```
**Features:** single-root invariant, branch and associate edge types, depth/breadth/width metrics, root-to-leaf path enumeration, orphan detection, cycle detection, type-frequency analysis, validation.
```mermaid
mindmap
elixir((Elixir))
concurrency(Concurrency)
processes[Processes]
ecosystem(Ecosystem)
beam)BEAM VM(
```
---
### Choreo.Planner — Project Planning
Model projects with tasks, milestones, users, and labels. Render as Kanban boards, Gantt charts, or dependency flowcharts.
```elixir
alias Choreo.Planner
alias Choreo.Planner.Analysis
project =
Planner.new("Launch v1")
|> Planner.add_milestone(:v1, title: "V1 Launch")
|> Planner.add_task(:design, title: "Design", status: :done, estimate_hours: 16)
|> Planner.add_task(:impl, title: "Implement", status: :in_progress, estimate_hours: 24)
|> Planner.add_task(:test, title: "Test", status: :backlog, estimate_hours: 8)
|> Planner.add_user(:alice, name: "Alice")
|> Planner.contains(:v1, :design)
|> Planner.contains(:v1, :impl)
|> Planner.contains(:v1, :test)
|> Planner.depends_on(:impl, :design)
|> Planner.depends_on(:test, :impl)
|> Planner.assign(:design, :alice)
# Analysis
Analysis.ready(project) #=> tasks with unmet dependencies satisfied
Analysis.blocked(project) #=> tasks blocked by incomplete dependencies
Analysis.critical_path(project, milestone: :v1)
#=> {:ok, [:design, :impl, :test], total_estimate: 48}
Analysis.bottlenecks(project) #=> high in-degree tasks
# Render
Planner.to_mermaid(project, syntax: :kanban)
Planner.to_mermaid(project, syntax: :gantt)
Planner.to_mermaid(project, syntax: :flowchart)
Planner.to_dot(project)
```
**Features:** Kanban (`kanban` / `kanban_compat`), Gantt, and flowchart rendering, milestone hierarchy, dependency and assignment tracking, ready/blocked/critical-path/bottleneck analysis, status and priority metadata.
```mermaid
kanban
title Launch v1
subgraph Backlog
test[Test<br/>estimate: 8h]
end
subgraph In Progress
impl[Implement<br/>estimate: 24h]
end
subgraph Done
design[Design<br/>estimate: 16h]
end
```
---
### Choreo.Sequence — Sequence Diagrams
Model ordered interactions between participants over time. Mermaid-native output with activation boxes, notes, and fragments.
```elixir
alias Choreo.Sequence
seq =
Sequence.new()
|> Sequence.add_actor(:user, label: "User")
|> Sequence.add_participant(:api, label: "API")
|> Sequence.add_participant(:db, label: "Database")
|> Sequence.message(:user, :api, label: "GET /accounts")
|> Sequence.activate(:api)
|> Sequence.message(:api, :db, label: "SELECT * FROM accounts")
|> Sequence.return(:db, :api, label: "rows")
|> Sequence.deactivate(:api)
|> Sequence.return(:api, :user, label: "200 OK")
Sequence.to_mermaid(seq)
Sequence.to_dot(seq) # Best-effort timeline fallback
```
**Features:** actors/participants, sync/async/return/self messages, activation boxes, notes, loops/alts/opts, missing-label detection, unbalanced activation detection.
```mermaid
sequenceDiagram
actor User
participant API
participant Database
User->>API: GET /accounts
activate API
API->>Database: SELECT * FROM accounts
Database-->>API: rows
API-->>User: 200 OK
deactivate API
```
---
### Choreo.ThreatModel — STRIDE Threat Modeling
Extend dataflow diagrams with security semantics. Auto-generate STRIDE threats based on element types, trust boundaries, and encryption status.
```elixir
alias Choreo.ThreatModel
alias Choreo.ThreatModel.Analysis
model =
ThreatModel.new()
|> ThreatModel.add_trust_boundary("internet", level: 0)
|> ThreatModel.add_trust_boundary("app", level: 2)
|> ThreatModel.add_external_entity(:user, boundary: "internet", label: "User")
|> ThreatModel.add_process(:api, boundary: "app", privilege: :admin, label: "API")
|> ThreatModel.add_data_store(:db, boundary: "app", sensitivity: :confidential, label: "DB")
|> ThreatModel.data_flow(:user, :api)
|> ThreatModel.data_flow(:api, :db, encrypted: true)
# Auto-generated threats
threats = Analysis.stride_threats(model)
#=> [%{id: "T1", category: :spoofing, target: :user, severity: :high, ...}, ...]
# Security analysis
Analysis.exposed_data_stores(model)
Analysis.high_risk_processes(model)
Analysis.unencrypted_boundary_flows(model)
# Sequence diagram
ThreatModel.to_sequence(model) #=> Mermaid sequenceDiagram string
```
**Features:** automated STRIDE threat generation with severity scoring, trust-boundary crossing detection, exposed-data-store identification, high-risk process detection, encrypted-flow detection, sequence diagram generation.
```mermaid
graph LR
classDef default color:white
user["User"]
api(("API"))
db[("DB")]
style user fill:#64748b,stroke:#46566d,stroke-width:3px
style api fill:#3b82f6,stroke:#1d64d8
style db fill:#f59e0b,stroke:#d78000
subgraph app ["app"]
api
db
end
subgraph internet ["internet"]
user
end
user --> api
api --> db
linkStyle 0 stroke-width:2px,stroke:#64748b,stroke-dasharray:5 5
linkStyle 1 stroke-width:2px,stroke:#64748b
```
---
### Choreo.ERD — Database Entity-Relationship Modeling
Model database schemas, validate data integrity constraints, render beautiful HTML-like tables or native Mermaid `erDiagram` syntaxes, and run advanced topological analysis.
```elixir
alias Choreo.ERD
alias Choreo.ERD.Analysis
erd =
ERD.new()
|> ERD.add_table(:users, columns: [
%{name: :id, type: :integer, key: :pk},
%{name: :email, type: :varchar, comment: "unique email"}
])
|> ERD.add_table(:posts, columns: [
%{name: :id, type: :integer, key: :pk},
%{name: :user_id, type: :integer, key: :fk},
%{name: :title, type: :varchar}
])
|> ERD.add_table(:comments, columns: [
%{name: :id, type: :integer, key: :pk},
%{name: :post_id, type: :integer, key: :fk},
%{name: :body, type: :text}
])
|> ERD.add_relationship(:users, :posts, cardinality: :one_to_many, label: "writes")
|> ERD.add_relationship(:posts, :comments, cardinality: :one_to_many, label: "has")
# Topological Analysis
Analysis.shortest_join_path(erd, :users, :comments) #=> {:ok, [:users, :posts, :comments]}
Analysis.cycles(erd) #=> [] (no circular foreign keys!)
Analysis.orphans(erd) #=> []
Analysis.table_degrees(erd) #=> %{users: %{in: 0, out: 1, total: 1}, ...}
```
**Features:** strict columns and relationship validation, customizable themed HTML record labels in DOT output, native Mermaid `erDiagram` generation, undirected BFS join path solver, DFS circular foreign key cycle detection, orphan and coupling metrics.
```mermaid
erDiagram
users {
integer id PK
varchar email "unique email"
}
posts {
integer id PK
integer user_id FK
varchar title
}
comments {
integer id PK
integer post_id FK
text body
}
users ||--o{ posts : "writes"
posts ||--o{ comments : "has"
```
---
## Themes & Rendering
All modules render to **DOT (Graphviz)** and **Mermaid.js** via a shared theming pipeline.
```elixir
# DOT output (Graphviz)
Choreo.to_dot(system, theme: :default)
Choreo.to_dot(system, theme: :dark)
# Mermaid.js output (GitHub, GitLab, Notion, Livebook)
Choreo.to_mermaid(system, theme: :default)
Choreo.to_mermaid(system, theme: :ocean)
# Custom theme
theme = Choreo.Theme.custom(
colors: %{database: "#ff0000", service: "#00ff00"},
graph_bgcolor: "#0f172a",
node_fontcolor: "white"
)
Choreo.to_dot(system, theme: theme)
Choreo.to_mermaid(system, theme: theme)
```
### Built-in themes
| Theme | Description |
|-------|-------------|
| `:default` | Type-coloured nodes, white background |
| `:dark` | Dark background with neon accents |
| `:minimal` | Monochrome wireframe, no fills |
| `:warm` | Sunset palette, warm background |
| `:forest` | Lush green palette, earth tones |
| `:ocean` | Deep blue palette, cool background |
### Per-module node types
| Module | Node types | Shapes |
|--------|-----------|--------|
| `Choreo` | database, cache, service, queue, ... | cylinder, diamond, box3d, cloud, folder |
| `Choreo.C4` | person, software_system, container, component | ellipse, box, rounded box, dashed box |
| `Choreo.FSM` | initial, normal, final | circle, doublecircle |
| `Choreo.Dataflow` | source, sink, transform, buffer, conditional, merge | house, invhouse, box3d, cylinder, diamond, trapezium |
| `Choreo.Dependency` | application, library, module, interface, test | box3d, cylinder, box, diamond, note |
| `Choreo.DecisionTree` | root, decision, outcome | diamond (double), diamond, rounded box |
| `Choreo.MindMap` | root, topic, subtopic, note | doublecircle, ellipse, rounded box, note |
| `Choreo.Planner` | task, milestone, user, label | rounded box, doublecircle, box, ellipse |
| `Choreo.Sequence` | actor, participant | sequence diagram lifelines |
| `Choreo.ThreatModel` | external_entity, process, data_store | box (double), circle, cylinder |
| `Choreo.Workflow` | start, end, task, decision, fork, join, compensation, event | circle, doublecircle, box3d, diamond, invhouse, house, note, cloud |
---
## Testing
```bash
mix test
```
All modules ship with comprehensive ExUnit test suites covering builders, analysis, rendering, and doctests:
**1043 tests** (327 doctests + 716 unit tests), 0 failures.
---
## Roadmap
- [x] Infrastructure architecture diagrams (`Choreo`)
- [x] Finite state machines with analysis (`Choreo.FSM`)
- [x] Dataflow / pipeline diagrams (`Choreo.Dataflow`)
- [x] Software dependency graphs (`Choreo.Dependency`)
- [x] Decision trees (`Choreo.DecisionTree`)
- [x] STRIDE threat modeling (`Choreo.ThreatModel`)
- [x] Task orchestration workflows (`Choreo.Workflow`)
- [x] Mind maps (`Choreo.MindMap`)
- [x] Schema validation for dataflow edges
- [x] Custom theme presets and per-node style overrides
- [x] Centrality metrics, cut vertices, k-core decomposition
- [x] Cross-module composition (e.g. embed a Dataflow inside a Choreo cluster)
- [x] Validation framework across all modules
- [x] Mermaid.js rendering for all modules
- [x] Mermaid sequence diagrams (`ThreatModel`)
- [x] C4 model architecture diagrams (`Choreo.C4`)
- [x] Sequence diagrams (`Choreo.Sequence`)
- [ ] Graph colouring algorithms
- [ ] Property-based testing suite
---
## License
MIT