Skip to content

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.

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.

Keep all I/O in the activity and the mapping pure, as the shipping lexicons do:

  1. Read live objects (aws cloudformation get-template, kubectl get -o json, …).
  2. Strip to declared shape by default — remove server-written fields (status, K8s managedFields, server metadata, provider bookkeeping annotations). Keep them when verbatim is set.
  3. Map to TemplateIR by reusing your import parser.
  4. Apply the selector (and the owned filter, 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.

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:

ChannelTargetsKeys
LabelsKubernetes, GCPapp.kubernetes.io/managed-by=chant, chant.intentius.io/stack, chant.intentius.io/env
TagsAWSchant:managed-by=chant, chant:stack, chant:env
TagsAzurechant-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)
}

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 marker
if (owned && !hasOwnershipMarker(labels, "label")) continue;
// describe: classify so the change set can gate delete
metadata.ownership = classifyOwnership(labels, "label");