Skip to content

codeanalyzer-iac

Beta

The codeanalyzer-iac backend analyzes infrastructure-as-code. Its command is caniac. It reads a repository, or a Neo4j graph that already holds one, and publishes one typed model of the infrastructure sources.

caniac is built on the Helm SDK, pinned to version 4.2.4. It never calls the helm CLI. Helm is the first dialect. Every other file stays an ordinary Artifact, and the backend never removes it.

Prebuilt binary for macOS and Linux:

Terminal window
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/codellm-devkit/codeanalyzer-iac/releases/latest/download/caniac-installer.sh | sh
Terminal window
caniac --app-name payments .
caniac --app-name payments charts/ deploy/values-prod.yaml
caniac --app-name payments --workspace-root . charts/api

Paths can be files or directories, and they can overlap. They are selection filters, not identity roots. Identity comes from --workspace-root, which defaults to the current directory. charts/api/values.yaml is therefore the same artifact whether you selected the repository, charts/, or that one file.

Every input file and every --config file must resolve below the workspace root. The backend does not follow symlinks out of it, and the walker skips .git.

With no path, the input is .. With no --app-name, the application takes the base name of the workspace root. In graph mode a name is mandatory.

LevelAddsNever does
L1Artifact classification, Helm facets, chart metadata, values keys, template definitions, template calls, value references, resource templates, source diagnosticsResolution or rendering
L2Chart membership, dependency resolution, named-template targets, value-reference targets, unresolved recordsRendering or any I/O
L3Render profiles, value layers, renders, rendered Kubernetes resources and addressesCluster access, install, upgrade, dependency fetching

-a/--analysis-level takes 1, 2, or 3, and defaults to 1. The levels are additive. Every fact that a lower level published is present, unchanged, at every higher level.

The graph always receives the deepest implemented level, which is L3 today. --emit cypher and --emit neo4j therefore raise the level to 3 unless you passed --analysis-level yourself. If you pass an explicit level below 3 with those targets, caniac rejects the run with an error. It does not silently raise the level.

Terminal window
caniac testdata/helm/l1-v2 --app-name payments --analysis-level 1 > analysis.json
caniac --workspace-root testdata/helm/profiles --app-name payments \
--config .codeanalyzer-iac.yaml --analysis-level 3 > analysis.json
--emitWritesTo stdoutTo -o DIR
json (filesystem default)The analysis documentCompact JSON, one trailing newlineanalysis.json
cypherThe replayable projection scriptThe scriptgraph.cypher
neo4j (graph-mode default)A reconciled generation over BoltNothingNothing
schemaThe embedded graph catalogschema.neo4j.jsonschema.neo4j.json

In filesystem mode, --emit cypher never consults the target graph. The script is projected from the analysis alone, so it has no hash-conflict guard against facts that are already in the graph. --emit neo4j does have that guard. In filesystem mode, --emit neo4j needs the target database. Give it with --neo4j-uri <uri> or with the NEO4J_URI environment variable.

--emit schema takes no input path. -f/--format accepts only json. msgpack is named so that the backend can reject it with a clear message instead of a bad parse. -j/--jobs bounds parallel parsing and rendering, and it defaults to the CPU count. The output is byte-identical whatever you set it to.

Stdout carries analysis data and nothing else. Errors go to stderr, one line each. A successful run leaves stderr empty. The backend discards the value-coalescing warnings that Helm emits. They reach neither stream.

A single positional Neo4j or Bolt URI switches caniac into graph mode.

Terminal window
caniac neo4j://localhost:7687 --app-name payments

In graph mode the backend reads the complete Artifact.source for every can://artifact/<app-name>/... node. It makes sure that each one matches the stored sha256. Then it writes its enrichment back onto those same nodes.

--app-name is required here. The URI names a database, not an application, and the backend does not select one for you. You cannot mix a URI and filesystem paths in one invocation. In graph mode --emit defaults to neo4j, so the command above reads and writes the same database over one connection. The connection reads --neo4j-user, --neo4j-password, and --neo4j-database, or the NEO4J_USERNAME, NEO4J_PASSWORD, and NEO4J_DATABASE environment variables. The user defaults to neo4j.

If a source does not match its recorded hash, that becomes a diagnostic on the artifact and the backend skips its semantic model. The recorded spans address the stored text, so a changed source makes every one of them wrong. The backend analyzes the other artifacts normally. There is no flag to accept a mismatch.

ExitMeaning
0The analysis completed
1A run-wide failure, or --strict with at least one error-severity diagnostic

Isolated failures are diagnostics inside the model, not process failures. An unparsable file, or a chart that the backend cannot render, still exits 0. A file that the backend cannot read as text stays a raw inventory entry, with a digest and no source. It reports IAC_SOURCE_NOT_TEXT at warning severity. A repository with binaries in it therefore still passes --strict.

A run that exits 1 publishes a document only when the failure comes after the write:

  • caniac reports an invalid configuration and a --strict failure after it writes the analysis, so those runs leave a document to read.
  • An unreadable input root, an unreachable graph, and a failed graph commit all end the run before that write, so they publish nothing.

--strict changes only the exit status. It never changes the document.

--eager removes the stale facts that this backend owns for the selected application: codeanalyzer-iac nodes and aliases, iac_* properties, facet labels, and IAC_* relationships. It never removes an Artifact.

The repository pins the accepted codeanalyzer-schema contract at commit b84428f. make schema-check and the pin-parity test keep every copy of that commit in step.