Skip to content

chant graph

chant graph [path] [--stacks] [--json]
chant graph [path] [--components] [--json]
chant graph [path] --format ir|mermaid|dot|layout [--detail 0..3] [--lens <kind>:<target>] [--up] [--down]
chant graph [path] --format layout [--node-sizes <json|-|@file>] [--layout-engine dagre|graphviz]
chant graph --format ir --live --env <name>
chant graph --format ir --live --env <name> --overlay

chant graph has four modes:

  • default — the Op dependency graph (edges from each Op’s depends field).
  • --stacks — the cross-stack apply-ordering graph.
  • --components — the component dependency graph, from each discovered component’s dependsOn.
  • --format ir|mermaid|dot|layout — the infrastructure graph chant resolves during synthesis, emitted for diagrams. This is the graph of your declared resources and the references between them.

The infra-graph formats are a pure function of lint-clean source — every node traces to a source file, every edge is a real cross-resource reference. They are lint-gated: chant refuses to emit a graph for source that does not pass chant lint, because the graph represents valid infrastructure.

--format ir --live --env <name> emits the graph of what’s actually deployed in that environment, not what source declares. It queries each lexicon’s live observation (describeResources) for the chant-managed resources and projects them into the same IR. Nodes carry their observed physicalId and ownership marker.

Terminal window
chant graph --format ir --live --env prod # the provisioned resources as IR

Edges are reconstructed from the live references: observed resources point at each other by physical id (a subnet’s VpcId, an ALB listener’s TargetGroupArn), and each lexicon ships a reference catalog that turns those into graph edges (peer references) and containment (subnet ∈ VPC). A reference whose target isn’t in the observed set is reported as dangling, never a wrong edge.

Scope today is managed-only — resources carrying chant’s ownership marker; it isn’t a full-account scan. Unlike the source graph, --live is not lint-gated (it reads the cloud, not source).

Edge reconstruction is only as rich as the observed node attributes. AWS’s plain describe-stack-resources metadata is too thin, so the AWS lexicon enriches from the deployed CloudFormation template (its {Ref}/{Fn::GetAtt} references, resolved to node ids) before reconstruction — so a managed AWS stack produces its topology. A lexicon without that enrichment yields nodes with fewer edges.

--live --overlay also reads your declared source and classifies every resource, tagging each node so a renderer colours the drift:

  • managed — declared and provisioned
  • foreign — provisioned but not declared (in the cloud, not in your source)
  • pending — declared but not yet provisioned (appended to the graph)
Terminal window
chant graph --format ir --live --env prod --overlay
--overlay-anchor <source|live> — which graph is the canvas
Section titled “--overlay-anchor <source|live> — which graph is the canvas”

The overlay merges two graphs; the anchor decides which one’s edges survive.

  • source (default) — the declared graph is the canvas, so its edges are kept. This matters because cross-substrate edges (an ECS service wired to a k8s workload) are a source-graph property: live edges are reconstructed per-substrate by identifier match and never cross providers, so the mixed topology exists only in the declared graph. Each declared node is tagged managed or pending and carries the observed physicalId/ownership; foreign resources are appended with any live edges that touch them.
  • live — the provisioned graph is the canvas, keeping its reconstructed live edges (the original behaviour). Use it when the provisioned graph itself is the subject and per-substrate islands are acceptable.
Terminal window
chant graph --format ir --live --env prod --overlay # source-anchored (keeps cross-substrate edges)
chant graph --format ir --live --env prod --overlay --overlay-anchor live # provisioned graph's own edges
Terminal window
chant graph ./infra --format mermaid # paste-able Mermaid flowchart (renders in GitHub)
chant graph ./infra --format ir # the graph IR as JSON (the contract for tools)
chant graph ./infra --format dot # Graphviz DOT (render with `dot -Tsvg`)
chant graph ./infra --format layout # node positions (dagre by default — no native dep)
FormatOutputNeeds
mermaidA Mermaid flowchart with one subgraph per lexicon. The zero-install default — renders in GitHub, docs, and browsers.nothing
irThe graph IR as JSON: nodes (id, kind, lexicon, attrs, sourceLoc, composite info), edges (real references, labelled with the consumer property), and groups. The stable contract downstream diagram tools consume.nothing
dotGraphviz DOT text, with lexicon clusters and labelled edges. Render directly with dot -Tsvg, or feed to a layout engine.nothing to produce; dot to render
layoutNode positions for a custom painter that does its own drawing: { width, height, nodes: [{id, x, y, w, h}] } (y-up, origin bottom-left).nothing (dagre); dot only with --layout-engine graphviz

The layout step exists for custom painters (the pinhole diagrammer is one): chant computes positions, the painter draws. By default it uses dagre — a pure-JS engine, so --format layout needs no native dependency.

A painter draws nodes at a real size (cards, icons, labels), so it can pass those footprints in and get spacing that fits them — no node overlap:

Terminal window
# {id: {w, h}} in layout units (≈ px). Inline, or '-' for stdin, or '@file.json'.
chant graph ./infra --format layout --node-sizes '{"vpc":{"w":180,"h":68}, ...}'
echo "$SIZES" | chant graph ./infra --format layout --node-sizes -

Without --node-sizes, nodes fall back to a default box (the layout still works, but a painter with larger nodes may need to space them itself).

To use Graphviz for layout instead of dagre (e.g. to match dot’s routing), pass --layout-engine graphviz — this is the only path that needs dot (brew install graphviz), and it honours lexicon clusters.

The detail dial controls how much of the graph each format shows. It is a transform over the same IR, so it works with every --format.

LevelNameShows
0stacksone node per lexicon; edges are cross-lexicon dependencies
1compositeseach composite instance collapsed to a single node
2declarablesevery resource (the default)
3attributesdeclarables, plus the referenced attribute on each edge
Terminal window
chant graph ./infra --format mermaid --detail 0 # high-level: lexicons only
chant graph ./infra --format mermaid --detail 1 # composites as single nodes
chant graph ./infra --format ir --detail 3 # every resource + attribute-level edges

A handful of composites at --detail 2 expands to every resource they declare — you describe a few, the graph shows the whole set. Turn the dial down to 0/1 for an overview.

Lenses focus the graph on a slice. They compose with --detail (the lens is applied first, then the detail tier).

LensKeeps
lexicon:<name>only that lexicon’s nodes
stack:<name>only one stack’s nodes (stacks map to lexicon partitions today)
blast:<node>the transitive neighbourhood of a node

For blast:, choose a direction (default is both):

  • --up — what the node depends on (its producers)
  • --down — what depends on the node (its dependents)
Terminal window
chant graph ./infra --format mermaid --lens lexicon:gcp
chant graph ./infra --format mermaid --lens blast:vpc --down # everything that depends on the VPC

A lens drops edges that would dangle and rebuilds group metadata, so the result is always a self-consistent graph. An unknown or no-match target exits non-zero with a clear message.

chant graph --stacks [path] renders the cross-stack apply-ordering graph instead. chant resolves cross-lexicon references during build and already knows the order — a stack that exports a value must apply before the stack that imports it. --stacks surfaces that as tool-agnostic data for your orchestrator. chant exposes the order; it does not drive the apply.

Terminal window
chant graph --stacks ./infra
chant graph --stacks ./infra --json

The output is the stacks, their dependency edges (consumer → producer), a topological apply order, and waves — levels whose stacks have no inter-dependency and may apply concurrently. --json emits the full graph (nodes, edges, order, waves, cycles). A dependency cycle is reported and exits non-zero.

Apply order (waves apply top-to-bottom; a wave's stacks are parallel-safe):
1. aws
2. k8s
Dependencies (consumer → producer):
k8s → aws

--components — component dependency order

Section titled “--components — component dependency order”

chant graph --components [path] renders the component dependency graph: every *.component.ts declaration under path, ordered by dependsOn. Unlike --stacks (which infers edges from cross-lexicon attribute references), a component’s dependency is always an explicit name in its own dependsOn list, so no reference inference is needed — the same dependsOn the interpret driver uses to order a real run.

Terminal window
chant graph --components ./infra
chant graph --components ./infra --json

The output is the same shape as --stacks: a topological order, waves (components with no inter-dependency, safe to run concurrently), and dependency edges (consumer → producer). --json emits the full result. A dependency cycle, or a dependsOn naming an undiscovered component, is reported and exits non-zero.

Deploy order (waves apply top-to-bottom; a wave's components are parallel-safe):
1. shared-alb, orders-table
2. search-service
Dependencies (consumer → producer):
search-service → shared-alb

With no format flag, chant graph discovers all *.op.ts files under the given path and prints the dependency edges declared via each Op’s depends field.

Terminal window
chant graph # Ops in the current directory
chant graph ./infra # Ops in a specific directory

Each dependency prints as one edge per line, <dependency> → <dependent> — the left Op must complete before the right Op can run. If no Ops declare dependencies the output is No Op dependencies; if no *.op.ts files are found, No Ops found.

CodeMeaning
0Success
1Discovery errors; lint errors (infra-graph formats); a dependency cycle (--stacks, --components) or unknown dependency (--components); an invalid --detail/--lens; malformed --node-sizes; or a layout-engine failure (e.g. missing dot with --layout-engine graphviz)
  • Ops guide — define Op dependencies with depends
  • chant lint — the gate the infra-graph formats run first
  • chant build — synthesize the artifacts the graph describes