Skip to content

Driving it from an agent

llms.txtlists every page for an agent
Optional: hand this page to your coding agentThe steps work by hand too.
Show the whole prompt
Clone https://github.com/INTENTIUS/behold and get me looking at a live
infrastructure graph in my browser.

I want the bundled demo. It needs Docker and no cloud account. Install what
it needs and start it, then tell me the URL.

Walk me through what I am looking at: what the colours mean, and how I make a
resource change colour while I watch.

When Docker is not running, say so. Show me the source graph on its own
instead.

For anything past the demo, read https://intentius.io/behold/llms.txt and
https://intentius.io/behold/using/agents/.

Never deploy with the Deploy button, `POST /api/apply` or `POST /api/ci/dispatch`. Gate approvals stay mine as well (`/api/ops/:name/signal/:gate`, `/api/workspace/gates/approve`, `/api/operator/approve/:op/:gate`). Op runs (`/api/ops/:name/run`) and rollbacks (`/api/rollback`) are mine too. Leave `--auto-sync` off, and keep a `behold preview` or `--public` serve read-only.

The SPA has no private channel: everything it shows rides plain JSON over HTTP, so an agent or a script can drive the same server headless. Start one and curl it:

Terminal window
npx @intentius/behold serve <chant-project-dir> # or preview / demo
curl -s http://localhost:4600/api | jq

GET /api is the front door. It lists the routes with a one-line description each, the server’s version, and a link to the full contract.

  1. observe. GET /api/graph returns { ir, svg, meta }. Every node in ir.nodes carries id, kind, lexicon, attrs, and sourceLoc. Drift status, when present, is attrs._status: good = managed, warn = foreign, accent = pending, neutral = unobserved, runtime = runtime child.
  2. focus. Narrow with query params: ?detail=0..3, ?components=1, ?logical=1, ?lens=blast:<id>&down=1, ?lens=lexicon:aws, plus the env / stack / tier / target axes the UI’s Scope tab exposes.
  3. go deeper. With an ?env=: /api/overlay is the live entity overlay, /api/diff slices per-node observed state / drift / field ownership, /api/reconcile summarizes the pending change set. /api/substrates reports substrate readiness, and /api/events (SSE) pushes changed / op / apply / pr so you re-pull only when something moved.
  4. inspect. A node’s sourceLoc.file is the typed source that declared it. To change the estate, edit there: chant is the source of truth, not behold. Provenance rides on the node too: attrs._release (the recorded deploy: run id, origin, commit, digest, actor, approver), attrs._carve (the Terraform address a carved entity came from), and on a Helm chart attrs._renderDrift.identity (whether the running release was deployed from this project’s render).

A directory that is not a chant project gets a structured {code: "no-project", error, remedy} from /api/graph. Every other precondition failure (lint, not-installed, tier, eval) takes the same shape, so an agent branches on code instead of parsing prose.

behold mcp is a stdio MCP server for a coding agent that wants to show the person what it found. chant answers and behold draws: capabilities returns chant’s capability catalog, ask returns chant’s answer to an estate question, and show and shot draw it.

Read capabilities first. It is chant’s capability catalog (chant search --capabilities --json, chant#4375), and it says which cap: questions this estate can be asked: each capability’s name, what it means, its parameters, the search term that asks it (cap:internet-reachable(22), ->[cap=network]), the questions it answers, what it does not evaluate, its rule, and whether each lexicon supplies it. Then ask with that term.

Terminal window
claude mcp add behold -- npx @intentius/behold mcp <project-dir>
Tool What it does Arguments
capabilities Returns chant’s capability catalog, the read contract’s capabilities document, verbatim (chant search --capabilities --json on one project or a Terraform folder, chant workspace search --capabilities --json on a declared workspace), through GET /api/capabilities. Needs chant 0.124.46 or newer. none
ask Returns chant’s search document for the query, verbatim (chant workspace search on a declared workspace, chant search --json on one project or a Terraform folder), with link to the view that draws it and drawn: false added. Never moves the tab. query, env, snapshot, plan, countBy, show
show Moves the person’s open behold tab to a view through POST /api/view. The tab says an agent moved it and keeps the view it left one Back away. With no tab open it answers the link to hand the person. query, env, snapshot, plan, countBy, member, zoom, focus, node
shot Returns a PNG of a view as MCP image content, then a text block with the link, through GET /api/shot. the same, plus width and height

query is chant’s search grammar, unchanged. env reads that environment live (chant search --live --env <env>), snapshot with env replays a recorded snapshot (latest, or a ref chant lists), and --explain is always on, for the near-misses behold draws. No tool takes --check-live, --check-snapshot, --ambient, --src or --fail-on-drift, and at is refused: in chant’s read contract it is a git revision. The tab and the PNG light the query’s matches, draw the edges on their paths and dim the rest; a cap: match’s path is the hops in its path’s because, one entry per capability it relied on, and those are drawn too; with no matches the whole graph is dimmed and the near-misses outlined. Without a query, shot frames the view’s own node or focus.

countBy (kind, module or attr:<name>) is chant’s --count-by: ask returns chant’s groups and total, and the tab and the PNG light each group’s cards and label the group with its count; the query may then be empty, to count the whole estate. plan: true answers at the plan depth (chant reads a saved terraform plan of each root), so attr:planAction=replace with show: "planActions,planReplacePaths,planActionReason" says what a change replaces and why, and the cards carry the plan facts. A bare Terraform folder needs no env for it; a chant project does. plan with snapshot is refused, naming both.

The server is a client of a running serve’s HTTP API and holds no query or view logic of its own. It finds the serve through --url or --port when given, then through a per-user registry under the OS temp directory (<tmpdir>/behold/serves/<pid>.json, holding {url, pid, projectDir, version, startedAt}), which every behold serve writes at start and removes at exit, then at http://127.0.0.1:4600. Nothing is written in the project. Before a tool uses a serve it reads GET /api/project. If that serve serves another project, show refuses and names both. With no serve running, show refuses with the command that starts one, and capabilities, ask and shot start behold in their own process for the call.

Every argument is listed and any other is refused, a credential-named one by name. A refusal comes back with isError and the route’s {error, code, remedy}. The server will not start with a tool whose name says it would deploy, apply, approve, run an Op, roll back or write, and a call to such a tool is refused. show changes what the tab draws and nothing else; shot writes no file.

behold performs no cloud write of its own. The mutating endpoints trigger Ops the project committed: POST /api/apply?env=&component=, POST /api/ops/:name/run, POST /api/ops/:name/signal/:gate, POST /api/rollback?to=. All are guarded (one write at a time), gated where the Op declares a gate, and locked out entirely in preview and static modes. An environment its member’s chant.config.ts designates to a forge (an executor on the environment entry, read through chant) answers those with 409 executor-forge; its one deploy is POST /api/ci/dispatch?env=, through the operator’s gh, followed on the dial, with GET /api/ci/run the persisted record and POST /api/ci/readopt the re-follow after a restart. For build, lint, lifecycle diff and op status, prefer chant’s own MCP tools over shelling.

The canonical version of this guide ships inside the npm package and lives at AGENTS.md. Hand it to your agent verbatim.

behold