Skip to content

chant lifecycle

chant lifecycle snapshot <env> [lexicon]
chant lifecycle show <env>
chant lifecycle diff <env> [--live]
chant lifecycle log [env]

chant lifecycle captures point-in-time snapshots of deployed infrastructure by querying the cloud provider API and saving the result to a git orphan branch. Snapshots enable drift detection — comparing what’s currently built against what was last deployed, and (with --live) against what’s actually in the cloud right now.

The environment name (e.g. dev, staging, prod) must be declared in chant.config.ts under environments.

For the conceptual model behind these commands — observational vs. authoritative state, resources vs. artifacts, when drift detection earns its keep — see Drift Detection.

Query the provider API for the current deployed state, then save a snapshot to the chant-state/<env> orphan branch.

Terminal window
chant lifecycle snapshot dev
chant lifecycle snapshot prod aws # snapshot only the aws lexicon

Print the latest saved snapshot for an environment.

Terminal window
chant lifecycle show dev

Two modes, depending on whether --live is set.

Default (digest mode). Build the current project, fingerprint the resource declarations, and compare against the digest stored in the last snapshot. Fast, offline, and useful as a fast-feedback check inside CI — but only catches changes you’ve made in source. Ignores the cloud entirely.

Terminal window
chant lifecycle diff dev

Reports added, removed, changed, and unchanged resources at the declaration level.

--live (drift mode). Query the cloud provider API right now via each lexicon’s describeResources() plugin method, then compare the live result against both the previous snapshot and the current build. This is the path that actually catches external mutations (someone scaled a node pool by hand, deleted a bucket, etc.).

Terminal window
chant lifecycle diff prod --live

Output is grouped into six categories per lexicon:

CategoryMeaning
missingDeclared in source, but not present in the cloud right now
orphanPresent in the cloud, but not declared in source
disappearedIn the previous snapshot, but gone now
newly observedDeclared and observed now, but not in any prior snapshot
driftedObserved in both snapshots; status, physical ID, or attributes changed
unchangedObserved in both snapshots; metadata identical

Drifted entries include attribute-level deltas (status, physicalId, lastUpdated, and each attributes.* key).

Promote the live diff to a typed, read-only change set. Where lifecycle diff --live reports six observation categories, lifecycle plan classifies each resource into one action you can act on:

Terminal window
chant lifecycle plan prod
chant lifecycle plan prod --json # emit the ChangeSet as JSON
ActionMeaning
createDeclared in source, absent from the cloud
updateDeclared and live, but the live config drifted
deleteA chant-owned resource that is live but no longer declared
adoptLive but undeclared, ownership not established — a candidate to pull back into source, never an auto-delete
noopDeclared and live with no drift, or already reconciled

create and update are precise from declared-vs-live. delete is only ever proposed for an undeclared resource whose live ownership marker (ownership marking) confirms it is chant’s; a foreign orphan is adopt, and an orphan with no marker data is adopt, never delete. Pass --owned to restrict the query to chant-owned resources.

The classification reads ownership from the live marker, never from the snapshot, so the snapshot never becomes load-bearing: the delete decision is identical whether or not a snapshot exists. The moment a mutation trusted the snapshot, the snapshot would have become an authoritative state file under a different name — which chant deliberately avoids.

lifecycle plan is strictly read-only: it builds, queries, and classifies. It never mutates the cloud and never deploys — chant build stays pure.

A single lexicon may report both resources (entity-keyed, via describeResources()) and artifacts (context-keyed, via listArtifacts()); lifecycle diff --live shows them in separate sections. Artifacts use a four-category two-way diff (no “declared” axis):

CategoryMeaning
artifacts addedObserved now, not in last snapshot
artifacts removedIn last snapshot, gone now
artifacts changedIn both; metadata changed
artifacts unchangedIn both; metadata identical

For the conceptual distinction between resources and artifacts, see Drift Detection — Resources and artifacts.

See the Runtime observation coverage matrix on the Lexicons overview page for the canonical version with query-mechanism notes. Summary:

LexicondescribeResources()listArtifacts()
AWS
Temporal
K8s
GCP (via Config Connector)
Azure
Helm
Docker
GitHub / GitLabN/A — see lexicon READMEs

Lexicons that implement neither method are warn-skipped — --live doesn’t fail the whole command for them.

The Temporal lexicon resolves its connection via the chant config: lifecycle snapshot <env> (or --live against <env>) looks up temporal.profiles.<env> in your chant.config.ts and falls back to temporal.defaultProfile. The same profile model is used by chant run. With #39’s entity-prop pass-through, namespaces/schedules/search-attributes are mapped back to chant entity names when the declared props.name (etc.) match the server-side identifier.

The K8s lexicon shells out to kubectl get <kind> <name> [-n <namespace>] -o json per declared entity. The cluster context comes from the user’s kubeconfig — there’s no separate profile model. Twenty common resource types (Deployment, Service, ConfigMap, Secret, Namespace, Pod, PVC, ServiceAccount, Job, CronJob, Ingress, NetworkPolicy, RBAC roles + bindings, ReplicaSet, StatefulSet, DaemonSet) are covered out of the box; CRDs and uncommon types are warn-skipped.

The GCP lexicon uses the same kubectl shell-out path against Config Connector CRDs. The entity type’s GVK derivation matches the serializer (GCP::Storage::Bucketstoragebucket.storage.cnrm.cloud.google.com), so any of the ~300 chant-supported GCP resources work without per-type configuration. Ready=True on the resource’s status is mapped to READY; otherwise the condition’s reason is surfaced.

The Azure lexicon shells out to az resource show --resource-group <env> --name <name> --resource-type <type> -o json per declared entity. The <env> argument is treated as the Azure resource group name. properties.provisioningState becomes the status. Top-level resources only — nested ARM types (e.g. Microsoft.Storage/storageAccounts/blobServices) are warn-skipped since az resource show doesn’t accept compound type names directly.

Snapshots are pushed to the orphan chant/lifecycle branch with git push --force-with-lease. If two operators (or a CI job + a human) take a snapshot for the same remote at the same time, the second push is rejected rather than silently overwriting the first. You’ll see:

error: Another snapshot completed for chant/lifecycle after this run started (env: prod).
hint: Pull and retry: `git fetch origin chant/lifecycle:chant/lifecycle` && `chant lifecycle snapshot prod`.

Pull the remote ref and re-run — the first snapshot is preserved, the second proceeds against the updated baseline.

List the history of snapshots. Omit env to show all environments.

Terminal window
chant lifecycle log # all environments
chant lifecycle log dev # just dev
CodeMeaning
0Success
1Error (invalid environment, API failure, no snapshot found)
  • chant build — build current output for diffing
  • Configuration — declare environments in chant.config.ts
  • Watching Lifecycle — wire lifecycle snapshot + lifecycle diff --live to a TemporalSchedule via the WatchOp composite
  • Implementing Observation — for lexicon authors, how to plug into --live via describeResources() / listArtifacts()