Choreo
Domain-specific diagram builders and graph analyzers on top of Yog.
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.
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?
- Installation
- Graph Analysis & Heatmaps
- Modules
- Choreo — Infrastructure Architecture
- Choreo.C4 — C4 Model Architecture
- Choreo.FSM — Finite State Machines
- Choreo.Dataflow — Pipeline Diagrams
- Choreo.Dependency — Software Dependency Graphs
- Choreo.DecisionTree — Classification Trees
- Choreo.MindMap — Concept Mapping
- Choreo.Planner — Project Planning
- Choreo.Sequence — Sequence Diagrams
- Choreo.ThreatModel — STRIDE Threat Modeling
- Choreo.Workflow — Task Orchestration
- Choreo.ERD — Database Entity-Relationship Modeling
- Choreo.UML — Class & Struct Diagrams
- Themes & Rendering
- Testing
- 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 |
# 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:
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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).
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
# 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
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
- Infrastructure architecture diagrams (
Choreo) - Finite state machines with analysis (
Choreo.FSM) - Dataflow / pipeline diagrams (
Choreo.Dataflow) - Software dependency graphs (
Choreo.Dependency) - Decision trees (
Choreo.DecisionTree) - STRIDE threat modeling (
Choreo.ThreatModel) - Task orchestration workflows (
Choreo.Workflow) - Mind maps (
Choreo.MindMap) - Schema validation for dataflow edges
- Custom theme presets and per-node style overrides
- Centrality metrics, cut vertices, k-core decomposition
- Cross-module composition (e.g. embed a Dataflow inside a Choreo cluster)
- Validation framework across all modules
- Mermaid.js rendering for all modules
- Mermaid sequence diagrams (
ThreatModel) - C4 model architecture diagrams (
Choreo.C4) - Sequence diagrams (
Choreo.Sequence) - Graph colouring algorithms
- Property-based testing suite
License
MIT