Implementing Live Export
Two opt-in capabilities let a lexicon participate in the cloud → code direction: live export (regenerate TypeScript from live config) and the ownership marker (tell chant-owned resources from foreign ones). They pair with observation (the cloud → report direction) but are deliberately separate from it.
exportResources()
Section titled “exportResources()”describeResources() returns scrubbed output metadata for diffing — you cannot regenerate a resource from it. exportResources() returns the full input config, suitable for regeneration:
exportResources?(options: { environment: string; selector?: ResourceSelector; // { type?, name? } owned?: boolean; // restrict to chant-owned resources verbatim?: boolean; // keep server-defaulted fields}): Promise<ExportedTemplate>;The result is the existing import IR (TemplateIR), branded as ExportedTemplate, so it feeds your lexicon’s templateGenerator() unchanged. The brand keeps a full-fidelity export — which may carry secrets — from flowing into the state code paths, which consume scrubbed metadata through the ObservationLexicon view.
Pattern
Section titled “Pattern”Keep all I/O in the activity and the mapping pure, as the shipping lexicons do:
- Read live objects (
aws cloudformation get-template,kubectl get -o json, …). - Strip to declared shape by default — remove server-written fields (
status, K8smanagedFields, server metadata, provider bookkeeping annotations). Keep them whenverbatimis set. - Map to
TemplateIRby reusing your import parser. - Apply the
selector(and theownedfilter, below).
Reference implementations: lexicons/aws/src/import/live-export.ts, lexicons/azure/src/import/live-export.ts, lexicons/k8s/src/import/live-export.ts, lexicons/gcp/src/import/live-export.ts.
The ownership marker
Section titled “The ownership marker”Ownership is what later lets delete be precise without a hosted state file. The marker is stamped at synthesis time into the target’s native metadata channel:
| Channel | Targets | Keys |
|---|---|---|
| Labels | Kubernetes, GCP | app.kubernetes.io/managed-by=chant, chant.intentius.io/stack, chant.intentius.io/env |
| Tags | AWS | chant:managed-by=chant, chant:stack, chant:env |
| Tags | Azure | chant-managed-by=chant, chant-stack, chant-env |
Stamp it from your serializer by reading context.ownership and merging into your tag/label channel — reuse the core helper:
import { ownershipEntries } from "@intentius/chant/ownership";
serialize(entities, outputs, context) { const ownership = context?.ownership; const labels = ownership ? ownershipEntries("label", ownership) : {}; // merge `labels` into each resource's metadata.labels (explicit labels win)}Querying it for owned
Section titled “Querying it for owned”Implement the owned filter on both describeResources and exportResources by reading the marker back:
import { hasOwnershipMarker, classifyOwnership } from "@intentius/chant/ownership";
// export: drop objects without the markerif (owned && !hasOwnershipMarker(labels, "label")) continue;
// describe: classify so the change set can gate deletemetadata.ownership = classifyOwnership(labels, "label");See also
Section titled “See also”- Implementing Observation — the
describeResources()/listArtifacts()side - Completeness Checklist — where these capabilities sit
- Live Import — the user-facing
chant import --from - Lifecycle Models — why ownership lives on the resource