Skip to content

Open your own project

behold opens any chant project. There is nothing to add to your project to get the source graph: no config file, no annotations, no plugin.

Terminal window
npx behold preview # the current directory
npx behold preview ../my-project # somewhere else

That serves your project’s declared graph at http://localhost:4600. It shells your project’s own chant binary, so what you see is what your project builds.

Terminal window
npx behold doctor # the current directory
npx behold doctor ../my-project --json

A read-only diagnosis of everything behold needs to serve the project well. One line each for the project’s kind, its own chant install and version, declared lexicons, the environments the picker will infer, the kube context chant binds versus your ambient one, substrate readiness, and committed Ops. Each is pass, warn or fail with a one-line fix; the exit code is non-zero only when something failed, so CI can gate on it. It starts no server and changes nothing.

The distinction it draws is the useful one: a fail is something that stops behold serving the project (no chant in the project’s node_modules is the common first-touch one, since behold shells your project’s chant). A warn costs you the live half rather than the graph: no environment declared, no cluster binding, an emulator that isn’t up.

To colour the graph by what is actually deployed, name an environment:

Terminal window
npx behold serve . --env prod

That needs whatever credentials reading your estate needs; behold uses your ambient environment and holds nothing of its own. Nodes become green (managed), amber (foreign), or blue (declared, not yet observed).

If a read fails, the affected nodes report unobserved rather than being painted absent. A resource behold could not look at is never the same claim as a resource that is not there.

Terminal window
npx behold serve . --env prod --poll 30

Re-queries every 30 seconds and pushes updates to the browser. Add --auto-sync to trigger a committed Op when drift appears: apply heals toward source, pull-request opens a PR adopting live into source. It is off by default and never approves a gate.

If your project’s source branches on a build parameter such as a deployment tier, declare it in a .behold.json at the project root and behold renders a picker for it:

{
"tiers": {
"envVar": "KMV_TIER",
"values": ["minimal", "prod", "prod-ha"]
}
}

envVar is the variable your source reads; behold sets it for the chant shell-out when a tier is picked. No file, or no tiers key, means no picker, which is the default for every project that does not opt in.

This is behold’s own config, deliberately separate from chant.config.ts so a viewer’s concerns stay out of the compiler’s.

Optional: a traffic level for live versus declared

Section titled “Optional: a traffic level for live versus declared”

behold can show what your estate costs as the account stands beside what the file says it should, with the difference. Both figures are predictions at a traffic level, and behold never picks one for you. Name it in .behold.json:

{
"behaviour": { "traffic": "100 rps, p50" }
}

The string goes to chant verbatim as chant graph --traffic. ?traffic= on /api/graph and /api/overlay overrides it for one request. With neither, behold asks for no prediction, which reads as “not looked” and is not a refusal.

With a level, /api/overlay asks chant twice for each member: once about the account (--live) and once about the file. The payload’s meta.behaviour carries live, declared and delta, per member and for the estate, and a card the account no longer holds keeps its declared figure under attrs._behaviourDeclared. The live figure moves with drift. The declared one moves only when a file under the member changes, so behold reads it once per edit.

The figures are an engine’s, reached by chant through CHANT_BEHAVIOUR_ENGINE. behold calls no engine and holds no key. A choudoufu or Terraform member also needs the optional @intentius/chant-lexicon-terraform installed beside behold, since that lexicon is what predicts a .tf root.

Drag a card or resize a containment box and behold remembers the offset, in your browser and (if the project directory is writable) in a .behold/layout.json sidecar next to it. That sidecar is the one file behold writes inside your project. It stores offsets rather than positions, so the graph underneath stays chant’s, and behold export bakes it into the exported SVGs.

Add .behold/ to the project’s .gitignore: unlike .behold.json above, which is config worth tracking, a layout is per-user state. Commit it only if you actually want everyone looking at the same arrangement.

Nothing, for the source graph. For the rest:

  • Live overlay. An environment your lexicons can read, and credentials for it.
  • Action buttons. Ops you committed. Sync appears if the project has an ApplyOp, Adopt per foreign node if it has a ReconcileOp, Run for anything else. A project with no Ops shows no buttons, by design.