{"id":32172724,"url":"https://github.com/team-alembic/ash_diagram","last_synced_at":"2026-02-21T17:34:18.062Z","repository":{"id":317122328,"uuid":"1029980181","full_name":"team-alembic/ash_diagram","owner":"team-alembic","description":"AshDiagram is a library for generating beautiful diagrams to visualize your Ash Framework applications.","archived":false,"fork":false,"pushed_at":"2026-01-01T04:19:33.000Z","size":1820,"stargazers_count":13,"open_issues_count":4,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-02-03T03:32:23.326Z","etag":null,"topics":["architecture","ash","ash-extension","c4-model","introspection"],"latest_commit_sha":null,"homepage":"https://hex.pm/packages/ash_diagram","language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/team-alembic.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-07-31T22:27:00.000Z","updated_at":"2025-10-28T16:33:00.000Z","dependencies_parsed_at":"2025-10-28T18:15:14.237Z","dependency_job_id":"f5c87464-774b-490e-8fe4-c7be9472016f","html_url":"https://github.com/team-alembic/ash_diagram","commit_stats":null,"previous_names":["team-alembic/ash_diagram"],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/team-alembic/ash_diagram","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/team-alembic%2Fash_diagram","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/team-alembic%2Fash_diagram/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/team-alembic%2Fash_diagram/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/team-alembic%2Fash_diagram/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/team-alembic","download_url":"https://codeload.github.com/team-alembic/ash_diagram/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/team-alembic%2Fash_diagram/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29688272,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-21T15:51:39.154Z","status":"ssl_error","status_checked_at":"2026-02-21T15:49:03.425Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["architecture","ash","ash-extension","c4-model","introspection"],"created_at":"2025-10-21T18:42:52.669Z","updated_at":"2026-02-21T17:34:13.047Z","avatar_url":"https://github.com/team-alembic.png","language":"Elixir","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cimg src=\"logos/logo.svg\" alt=\"AshDiagram Logo\" width=\"250\"\u003e\n\n\u003c!-- ex_doc_ignore_start --\u003e\n# AshDiagram\n\u003c!-- ex_doc_ignore_end --\u003e\n\n**AshDiagram** is an Elixir library for generating beautiful, interactive diagrams to visualize your [Ash Framework](https://ash-hq.org/) applications. Generate Entity Relationship diagrams, Class diagrams, C4 Architecture diagrams, and Policy flowcharts directly from your Ash resources and domains.\n\n## Features\n\n- 🔗 **Entity Relationship Diagrams** - Visualize relationships between your Ash resources\n- 📦 **Class Diagrams** - Show the structure of your resources with attributes and relationships\n- 🏗️ **C4 Architecture Diagrams** - Display system architecture at different abstraction levels\n- 🔐 **Policy Diagrams** - Understand authorization flows with flowchart representations\n- 🎨 **Multiple Output Formats** - Render to PNG, SVG, or Mermaid markdown\n- ⚡ **Clarity Integration** - Works seamlessly with the Clarity introspection framework\n- 🔄 **Automatic Generation** - Generate diagrams at application, domain, and resource levels\n\n## Supported Diagram Types\n\n### Entity Relationship Diagrams\nGenerate ER diagrams showing your resources and their relationships:\n- Resource entities with attributes\n- Relationship cardinalities\n- Foreign key relationships\n\n### Class Diagrams\nCreate UML-style class diagrams of your resources:\n- Resource attributes and types\n- Methods (actions)\n- Inheritance and composition relationships\n\n### C4 Architecture Diagrams\nVisualize system architecture using the C4 model:\n- Context diagrams showing system boundaries\n- Container diagrams showing high-level technology choices\n- Component diagrams showing internal structure\n\n### Policy Flowcharts\nUnderstand authorization logic through flowchart diagrams:\n- Policy conditions and rules\n- Decision trees and access controls\n- Authorization flow visualization\n\n## Installation\n\nAdd `ash_diagram` to your list of dependencies in `mix.exs`:\n\n```elixir\ndef deps do\n  [\n    {:ash_diagram, \"~\u003e 0.1.0\"}\n  ]\nend\n```\n\nFor rendering capabilities, you'll also want to include optional dependencies:\n\n```elixir\ndef deps do\n  [\n    {:ash_diagram, \"~\u003e 0.1.0\"},\n    {:ex_cmd, \"~\u003e 0.15.0\"},  # For CLI rendering\n    {:req, \"~\u003e 0.5.15\"}      # For Mermaid.ink rendering\n  ]\nend\n```\n\n## Usage\n\n### Basic Usage\n\nGenerate and render diagrams programmatically:\n\n```elixir\n# Generate an Entity Relationship diagram for a domain\ndiagram = AshDiagram.Data.EntityRelationship.for_domains([MyApp.Accounts])\nmermaid_code = AshDiagram.compose(diagram)\n\n# Render to PNG\npng_data = AshDiagram.render(diagram, format: :png)\nFile.write!(\"diagram.png\", png_data)\n```\n\n### Integration with Clarity\n\nAshDiagram includes a [`clarity`](https://hex.pm/packages/clarity) introspector that automatically generates diagrams for your Ash applications.\n\n### Renderer Configuration\n\nAshDiagram automatically detects and uses available renderers. You can configure a specific renderer in your application config:\n\n```elixir\n# config/config.exs\nconfig :ash_diagram, :renderer, AshDiagram.Renderer.CLI\n# or\nconfig :ash_diagram, :renderer, AshDiagram.Renderer.MermaidInk\n```\n\n#### Built-in Renderers\n\n**CLI Renderer** (requires `ex_cmd` and `mmdc` command in PATH):\n- Uses local Mermaid CLI installation\n- Supports PNG, SVG, and PDF formats\n- Best for server environments with Node.js installed\n\n**Mermaid.ink Renderer** (requires `req`):\n- Uses the online Mermaid.ink service\n- Supports SVG and PNG formats\n- Best for development and when CLI tools aren't available\n\n#### Custom Renderers\n\nYou can implement custom renderers by implementing the `AshDiagram.Renderer` behaviour:\n\n```elixir\ndefmodule MyApp.CustomRenderer do\n  @behaviour AshDiagram.Renderer\n\n  @impl true\n  def supported?(), do: true\n\n  @impl true\n  def render(diagram, options) do\n    # Your custom rendering logic\n  end\nend\n\n# Configure it\nconfig :ash_diagram, :renderer, MyApp.CustomRenderer\n```\n\n#### Diagram Extensions\n\nYou can extend generated diagrams with custom data by implementing the `AshDiagram.Data.Extension` behaviour. This allows you to add additional elements to any diagram type.\n\n```elixir\ndefmodule MyApp.DiagramExtension do\n  @behaviour AshDiagram.Data.Extension\n\n  use Spark.Dsl.Extension\n\n  @impl AshDiagram.Data.Extension\n  def supports?(AshDiagram.Data.Architecture), do: true\n  def supports?(_creator), do: false\n\n  @impl AshDiagram.Data.Extension\n  def extend_diagram(AshDiagram.Data.Architecture, %AshDiagram.C4{} = diagram) do\n    # Add custom element to C4 architecture diagrams\n    custom_element = %AshDiagram.C4.Element{\n      type: :system,\n      external?: true,\n      alias: \"external_system\",\n      label: \"External System\"\n    }\n    %{diagram | entries: [custom_element | diagram.entries]}\n  end\nend\n```\n\n**Extension Discovery**: Extensions are discovered by adding them to your Ash domains and resources:\n\n```elixir\n# Add to domain\ndefmodule MyApp.Accounts do\n  use Ash.Domain,\n    extensions: [MyApp.DiagramExtension]\n\n  resources do\n    resource MyApp.Accounts.User\n  end\nend\n\n# Add to specific resources\ndefmodule MyApp.Accounts.User do\n  use Ash.Resource,\n    domain: MyApp.Accounts,\n    extensions: [MyApp.DiagramExtension]\n\n  # ... resource definition\nend\n```\n\nExtensions are automatically collected from all domains and resources when generating diagrams.\n\n## Examples\n\n### Entity Relationship Diagram\n\n```elixir\nalias AshDiagram.Data.EntityRelationship\n\n# Generate for specific domains\ndiagram = EntityRelationship.for_domains([MyApp.Accounts, MyApp.Blog])\n\n# Generate for entire application\ndiagram = EntityRelationship.for_applications([:my_app])\n\n# Render as PNG\npng_data = AshDiagram.render(diagram, format: :png)\n```\n\n### Class Diagram\n\n```elixir\nalias AshDiagram.Data.Class\n\n# Generate class diagram\ndiagram = Class.for_domains([MyApp.Accounts])\nsvg_data = AshDiagram.render(diagram, format: :svg)\n```\n\n### Architecture Diagram\n\n```elixir\nalias AshDiagram.Data.Architecture\n\n# Generate C4 architecture diagram\ndiagram = Architecture.for_applications([:my_app])\nmarkdown = AshDiagram.compose_markdown(diagram)\n```\n\n### Policy Diagram\n\n```elixir\nalias AshDiagram.Data.Policy\n\n# Generate policy flowchart for a resource\ndiagram = Policy.for_resource(MyApp.Accounts.User)\nmermaid = AshDiagram.compose(diagram)\n```\n\n### Optional Dependencies\n\n- `ex_cmd ~\u003e 0.15.0` - Required for CLI rendering\n- `req ~\u003e 0.5.15` - Required for Mermaid.ink rendering\n- `clarity ~\u003e 0.1.2` - For automatic diagram generation integration\n\n## Contributing\n\nContributions are welcome! Please feel free to submit a Pull Request.\n\n## Links\n\n- [Documentation](https://hexdocs.pm/ash_diagram)\n- [Ash Framework](https://ash-hq.org/)\n- [Mermaid.js](https://mermaid.js.org/)\n- [C4 Model](https://c4model.com/)\n\n## Example Diagrams\n\nExamples taken from the [`tunez`](https://github.com/sevenseacat/tunez) starter\napp of the [Ash Framework](https://pragprog.com/titles/ldash/ash-framework/)\nbook.\n\n\u003c!-- tabs-open --\u003e\n\n### Class\n\n```mermaid\nclassDiagram\n  class `Tunez.Music.Album`[\"Music.Album\"] {\n    +UUID id\n    +String name\n    +Integer year_released\n    +?String cover_image_url\n    +read() : read~Music.Album~\n    +destroy() : destroy~Music.Album~\n    +create(?Map[] tracks) : create~Music.Album~\n    +update(?Map[] tracks) : update~Music.Album~\n  }\n  class `Tunez.Music.Artist`[\"Music.Artist\"] {\n    +UUID id\n    +String name\n    +?String[] previous_names\n    +?String biography\n    +UtcDatetimeUsec inserted_at\n    +UtcDatetimeUsec updated_at\n    +Boolean followed_by_me\n    +unknown album_count\n    +unknown latest_album_year_released\n    +unknown follower_count\n    +read() : read~Music.Artist~\n    +create() : create~Music.Artist~\n    +search(?CiString query) : read~Music.Artist~\n    +update() : update~Music.Artist~\n    +destroy() : destroy~Music.Artist~\n  }\n  class `Tunez.Music.ArtistFollower`[\"Music.ArtistFollower\"] {\n    +read() : read~Music.ArtistFollower~\n    +for_artist(UUID artist_id) : read~Music.ArtistFollower~\n    +create() : create~Music.ArtistFollower~\n    +destroy(UUID artist_id) : destroy~Music.ArtistFollower~\n  }\n  class `Tunez.Music.Track`[\"Music.Track\"] {\n    +UUID id\n    +String name\n    +Integer number\n    +String duration\n    +destroy() : destroy~Music.Track~\n    +read() : read~Music.Track~\n    +create(String duration) : create~Music.Track~\n    +update(String duration) : update~Music.Track~\n  }\n  `Tunez.Music.Album` \"*\" o--* \"0..1\" `Tunez.Music.Track`\n  `Tunez.Music.Album` \"0..1\" *--o \"*\" `Tunez.Music.Artist`\n\n```\n\n\n### Entity Relationship\n\n```mermaid\nerDiagram\n  \"Tunez.Music.Album\"[\"Music.Album\"] {\n    UUID id\n    String name\n    Integer year_released\n    String？ cover_image_url\n  }\n  \"Tunez.Music.Artist\"[\"Music.Artist\"] {\n    UUID id\n    String name\n    String[]？ previous_names\n    String？ biography\n    UtcDatetimeUsec inserted_at\n    UtcDatetimeUsec updated_at\n    Boolean followed_by_me\n    unknown album_count\n    unknown latest_album_year_released\n    unknown follower_count\n  }\n  \"Tunez.Music.ArtistFollower\"[\"Music.ArtistFollower\"] {\n  }\n  \"Tunez.Music.Track\"[\"Music.Track\"] {\n    UUID id\n    String name\n    Integer number\n    String duration\n  }\n  \"Tunez.Music.Album\" }o--o| \"Tunez.Music.Track\" : \"\"\n  \"Tunez.Music.Album\" |o--o{ \"Tunez.Music.Artist\" : \"\"\n\n```\n\n### C4 Architecture\n\n```mermaid\nC4Context\n  System_Boundary(\"beam\", \"BEAM\") {\n    System_Boundary(\"tunez\", \"tunez Application\") {\n      System_Boundary(\"tunez_accounts\", \"Accounts\") {\n        System(\"tunez_accounts_token\", \"Accounts.Token\", \"Resource with 12 actions, 0 relationships\")\n        System(\"tunez_accounts_user\", \"Accounts.User\", \"Resource with 14 actions, 2 relationships\")\n        System(\"tunez_accounts_notification\", \"Accounts.Notification\", \"Resource with 4 actions, 2 relationships\")\n      }\n      System_Boundary(\"tunez_music\", \"Music\") {\n        System(\"tunez_music_artist\", \"Music.Artist\", \"Resource with 5 actions, 5 relationships\")\n        System(\"tunez_music_album\", \"Music.Album\", \"Resource with 4 actions, 5 relationships\")\n        System(\"tunez_music_track\", \"Music.Track\", \"Resource with 4 actions, 1 relationships\")\n        System(\"tunez_music_artist_follower\", \"Music.ArtistFollower\", \"Resource with 4 actions, 2 relationships\")\n      }\n    }\n    System_Boundary(\"ash_postgres\", \"ash_postgres Application\") {\n      SystemDb(\"\", \"\", \"\")\n    }\n  }\n  Rel(\"tunez_music_album\", \"tunez_music_track\", \"tracks\", \"has_many relationship\")\n  Rel(\"tunez_music_artist\", \"tunez_music_album\", \"albums\", \"has_many relationship\")\n  Rel(\"tunez_accounts_token\", \"\", \"uses\", \"Stores data\")\n  Rel(\"tunez_accounts_user\", \"\", \"uses\", \"Stores data\")\n  Rel(\"tunez_accounts_notification\", \"\", \"uses\", \"Stores data\")\n  Rel(\"tunez_music_artist\", \"\", \"uses\", \"Stores data\")\n  Rel(\"tunez_music_album\", \"\", \"uses\", \"Stores data\")\n  Rel(\"tunez_music_track\", \"\", \"uses\", \"Stores data\")\n  Rel(\"tunez_music_artist_follower\", \"\", \"uses\", \"Stores data\")\n\n```\n\n### Resource Policy\n\n```mermaid\nflowchart TD\n  start((Policy Evaluation Start))\n  subgraph at_least_one_policy [at least one policy applies]\n    at_least_one_policy_check{\"actor.role == :admin or action.type == :read or action == :create or action.type in [:update, :destroy]\"}\n  end\n  0_conditions{\"actor.role == :admin\"}\n  0_checks_0{\"always true\"}\n  1_conditions{\"action.type == :read\"}\n  1_checks_0{\"always true\"}\n  2_conditions{\"action == :create\"}\n  2_checks_0{\"actor.role == :editor\"}\n  3_conditions{\"action.type in [:update, :destroy]\"}\n  3_checks_0{\"can_manage_album?\"}\n  subgraph results [Results]\n    authorized((Authorized))\n    forbidden((Forbidden))\n  end\n  0_conditions --\u003e|True| 0_checks_0\n  0_conditions --\u003e|False| 1_conditions\n  0_checks_0 --\u003e|True| authorized\n  0_checks_0 --\u003e|False| 1_conditions\n  1_conditions --\u003e|True| 1_checks_0\n  1_conditions --\u003e|False| 2_conditions\n  1_checks_0 --\u003e|True| 2_conditions\n  1_checks_0 --\u003e|False| forbidden\n  2_conditions --\u003e|True| 2_checks_0\n  2_conditions --\u003e|False| 3_conditions\n  2_checks_0 --\u003e|True| 3_conditions\n  2_checks_0 --\u003e|False| forbidden\n  3_conditions --\u003e|True| 3_checks_0\n  3_conditions --\u003e|False| authorized\n  3_checks_0 --\u003e|True| authorized\n  3_checks_0 --\u003e|False| forbidden\n  start --\u003e at_least_one_policy_check\n  at_least_one_policy_check --\u003e|False| forbidden\n  at_least_one_policy_check --\u003e|True| 0_conditions\n  classDef authorized fill:#e8f5e8,stroke:#4CAF50,stroke-width:2px\n  classDef forbidden fill:#ffebee,stroke:#f44336,stroke-width:2px\n  classDef condition fill:#e3f2fd,stroke:#2196F3\n  class authorized authorized\n  class forbidden forbidden\n  class start condition\n\n```\n\n### Resource Policy Simulation\n\n```mermaid\nflowchart TD\n  start((Start))\n  authorized((Authorized))\n  check_{\"'action == :archive'\"}\n  check_l{\"'action.type == :create'\"}\n  check_ll{\"'action.type == :destroy'\"}\n  check_lll{\"'action.type == :read'\"}\n  check_llll{\"'action.type == :update'\"}\n  check_llllr{\"'actor is present'\"}\n  check_llllrl{\"'actor.role == :admin'\"}\n  check_llllrll{\"'(actor(:role) == :manager) and relates_to_actor_via(:members)'\"}\n  check_llllrr{\"'actor.role == :admin'\"}\n  check_lllr{\"'actor is present'\"}\n  check_llr{\"'actor is present'\"}\n  check_llrr{\"'actor.role == :admin'\"}\n  check_llrrr{\"'record.members.users == actor'\"}\n  check_r{\"'actor is present'\"}\n  check_rl{\"'actor.role == :admin'\"}\n  check_rll{\"'(actor(:role) == :manager) and relates_to_actor_via(:members)'\"}\n  check_rllr{\"'(actor(:role) in [:admin, :manager]) and is_nil(archived_at)'\"}\n  check_rr{\"'actor.role == :admin'\"}\n  start --\u003e check_\n  check_ --\u003e|No| check_l\n  check_ --\u003e|Yes| check_r\n  check_l --\u003e|No| check_ll\n  check_l --\u003e|Yes| check_llllr\n  check_ll --\u003e|No| check_lll\n  check_ll --\u003e|Yes| check_llr\n  check_lll --\u003e|No| check_llll\n  check_lll --\u003e|Yes| check_lllr\n  check_llll --\u003e|Yes| check_llllr\n  check_llllr --\u003e|No| check_llllrl\n  check_llllr --\u003e|Yes| check_llllrr\n  check_llllrl --\u003e|No| check_llllrll\n  check_llllrll --\u003e|Yes| authorized\n  check_llllrr --\u003e|No| check_llllrll\n  check_llllrr --\u003e|Yes| authorized\n  check_lllr --\u003e|Yes| authorized\n  check_llr --\u003e|Yes| check_llrr\n  check_llrr --\u003e|Yes| check_llrrr\n  check_llrrr --\u003e|No| authorized\n  check_r --\u003e|No| check_rl\n  check_r --\u003e|Yes| check_rr\n  check_rl --\u003e|No| check_rll\n  check_rll --\u003e|Yes| check_rllr\n  check_rllr --\u003e|Yes| authorized\n  check_rr --\u003e|No| check_rll\n  check_rr --\u003e|Yes| check_rllr\n  classDef authorized fill:#e8f5e8,stroke:#4CAF50,stroke-width:2px\n  classDef forbidden fill:#fde8e8,stroke:#f44336,stroke-width:2px\n  class authorized authorized\n  class forbidden forbidden\n  classDef clause fill:#f3e5f5,stroke:#9C27B0,stroke-width:2px\n  classDef or_node fill:#fff3e0,stroke:#FF9800\n```\n\n\u003c!-- tabs-close --\u003e\n\n## License\n\nCopyright 2025 Alembic Pty Ltd\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fteam-alembic%2Fash_diagram","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fteam-alembic%2Fash_diagram","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fteam-alembic%2Fash_diagram/lists"}