Config File
chant uses a chant.config.ts file at the project root for configuration.
Minimal Config
Section titled “Minimal Config”export default { lexicons: ["aws"],};Full Config
Section titled “Full Config”export default { lexicons: ["aws"],
lint: { extends: ["@intentius/chant/lint/presets/strict"],
rules: { "EVL001": "error", "COR002": "warning", "COR009": "off", },
overrides: [ { files: ["src/legacy/**/*.ts"], rules: { "EVL001": "warning", "COR005": "off", }, }, ],
plugins: ["./lint-rules/org-standards.ts"], },};Fields
Section titled “Fields”lexicons
Section titled “lexicons”An array of the lexicons the project uses. A bare name loads the package @intentius/chant-lexicon-<name>:
lexicons: ["aws", "k8s"],An entry can instead name a module in the project, for a lexicon you write yourself:
lexicons: ["fly", { name: "site", module: "./lexicon/index.ts" }],The path is resolved against the directory holding chant.config.ts, and no package is needed. An optional root names the directory holding the lexicon’s other files, which fold trusts the way it trusts a package’s subpaths. See A lexicon in your own project for what the module has to export and the bounds on root.
exclude and include
Section titled “exclude and include”Glob lists that change which files discovery reads. Every command that walks the project honours them. A file exclude names is never imported or linted, and Op discovery skips it too. For a single hand-written file, the skip marker comment does the same without touching the config.
exclude: ["ops/**", "scripts"],include: ["ops/stack.ts"],- Patterns are relative to the directory holding
chant.config.ts, use forward slashes, and are matched with picomatch.*and**match dot files and dot directories too. - A pattern matches a file when it matches the file’s path or the path of any directory above it, so
ops,ops/**andops/*.tsall skipops/run.ts. - A file is skipped when an
excludepattern matches it and noincludepattern does.includealways wins overexclude. includenever narrows discovery by itself. A file nothing skips is discovered whether or notincludenames it. To read onlysrc/, writeexclude: ["**"], include: ["src"].includealso re-admits what the discovery walk skips by default, such as a child project:include: ["src"]reads asrc/that holds its ownchant.config.ts.- Neither key reaches into
node_modulesor.git. Test files and workspace members stay skipped, as do files carrying a marker. - Each entry must be a non-empty string. Anything else fails the config load with an error naming the key.
chant auditreads the keys from achant.config.jsononly. It never runs achant.config.tsfrom the repository it audits.
ownership
Section titled “ownership”Opt-in cloud-side ownership marking. When stack is set, the serializer stamps a chant ownership marker carrying this stack (and optional env) identity onto every supported resource at synthesis time:
ownership: { stack: "billing", // stamped onto every resource (enables marking) env: "prod", // optional environment identity // enabled: false, // set to disable without removing the block}env can also reference a declared build-time parameter, so the marker takes the value the build was invoked with rather than a value fixed when the config loaded:
ownership: { stack: "billing", env: { param: "env" } },buildParams: { env: { type: "string", default: "dev", env: "BILLING_ENV" },},With that, chant build --param env=prod stamps env=prod into the marker and binds params.env to "prod" for source in the same resolution. A reference to a parameter the config does not declare, or one with no value for the build, is a build error. A literal env next to an env parameter that resolved to something else gets a warning naming both values.
The marker is what lets a later delete be precise without an authoritative state file. Ownership lives on the cloud resource, so chant has no file to host or lock. It is stamped into each target’s native metadata channel:
| Channel | Targets | Keys |
|---|---|---|
| Labels | Kubernetes, GCP (Config Connector) | 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 |
The marker carries stack identity, not just managed=true, so one stack never mistakes another’s resources for its own. A resource is chant’s when it carries chant’s marker, even when other tools (ArgoCD, Terraform) stamp it too. Explicit per-resource tags/labels win on key collisions. The markers are standard tags/labels, so you can stop using chant and leave nothing proprietary behind.
telemetry
Section titled “telemetry”Telemetry attribution (#2558). A lexicon that declares workloads stamps the attributes that name the declaration (chant.workspace, chant.member, chant.decl, service.name and deployment.environment.name) as OTEL_SERVICE_NAME and OTEL_RESOURCE_ATTRIBUTES in each workload’s environment. The docker, k8s and fly lexicons do so today.
telemetry: { attribution: true },Inside a workspace attribution is on, and attribution: false turns it off. Outside one it is off, and attribution: true turns it on with the attributes that need no workspace. A project with neither a workspace declaration nor this key builds the same bytes as before. See how a reader joins a span.
Defaults for chant run and its subcommands (#2523). run.on names the runtime that hosts the project’s Op runs when --on is not given: a configured lexicon that declares an opRuntime, or "local" for the built-in runtime.
lexicons: ["aws", "mylexicon"],run: { on: "mylexicon" },An explicit --on takes precedence over run.on, and --on local selects the built-in runtime for one invocation. When run.on names a lexicon that is not configured or has no opRuntime, chant run refuses with the message --on would give. The run block accepts only on, and a block that fails validation is refused rather than ignored.
codegen
Section titled “codegen”Where chant generate writes the typed code it generates from sources the config declares (k8s.crds, helm.charts). Each lexicon writes to its own subdirectory of codegen.outDir, which is relative to the project root and defaults to src/generated.
codegen: { outDir: "infra/generated" },environments
Section titled “environments”An optional array of environment names the project deploys to. Each environment is a name chant passes to the commands below, with no config block of its own.
environments: ["local", "staging", "prod"],--env <name> selects one and defaults to local when omitted. The selected environment is what chant lifecycle diffs against, the environment organizational policy evaluates against during chant build, and the key the component release ledger records each deploy under, as in chant run --components <name> --env <env> and chant components status <env>. The env stamped into the ownership marker is a separate setting, ownership.env, which can reference a build parameter so the two stay one value.
A * in an entry makes it a glob pattern that declares a family of environments at once, such as per-PR previews or per-suite test copies:
environments: ["staging", "prod", "pr-*"],--env pr-42 (and, with a param-bound ownership.env, --param env=pr-42) is then legal; any name no entry covers is still rejected. Matching is literal first, then by pattern; * matches any run of characters and is the whole pattern syntax, with no ?, character classes or regex. A pattern entry can carry an endpoint like any other entry, and a literal entry always wins over a pattern for the same name.
To point an environment’s --live reads at a specific endpoint, give it an object with name and endpoint instead of a bare name:
environments: [ "prod", { name: "floci", endpoint: "http://localhost:4566" },],This is for a local emulator (Floci, mudflaps) or any non-default endpoint. Without it, --live --env floci shells out to each lexicon’s CLI with whatever the shell exports (AWS_ENDPOINT_URL for AWS, FLY_FLAPS_BASE_URL for Fly). If that var isn’t set, the read targets real AWS/Fly instead of the emulator and returns an empty result with no error. With the endpoint declared, chant sets that var for the read unless the shell already sets it. A value from the shell always wins, so a project that already exports it sees no change. If a --live describe still returns zero resources for an environment that has declared entities, chant warns instead of reporting an empty result.
Promotion order and canary
Section titled “Promotion order and canary”The order of the literal entries in environments is the member’s promotion order: a release moves from each environment to the next, so with ["dev", "staging", "prod"] a release goes to staging after dev and to prod after staging. A pattern entry such as pr-* names no environment and is not a step. A release belongs to one member, so the member’s chant.config.ts owns the order. A component’s own environments entries say where its release is hosted in each environment and do not set the order.
chant components promote --to <env> defaults --from to the step before --to. It refuses a promotion that skips a step unless --allow-skip is given. See the promotion order for the refusal codes. A member with no environments has no order, so promote needs --from and refuses nothing. chant workspace status --json lists the order in each member’s promotionOrder.
An entry can mark a step as the canary step with canary: true. The bare-name form works unchanged beside it:
environments: [ "dev", { name: "staging", canary: true }, "prod",],The canary is declared and shown, not enforced. chant records it in promotionOrder and does nothing else with it yet. Promote deploys to a canary step like any other step, with no traffic split or hold.
lint.extends
Section titled “lint.extends”An array of preset configuration paths to inherit from:
extends: ["@intentius/chant/lint/presets/strict"]lint.rules
Section titled “lint.rules”A map of rule ID to severity or [severity, options] tuple:
rules: { "EVL001": "error", "COR009": ["warning", { max: 12 }],}lint.overrides
Section titled “lint.overrides”File-specific rule overrides using glob patterns:
overrides: [ { files: ["src/legacy/**/*.ts"], rules: { "EVL001": "warning" }, },]lint.plugins
Section titled “lint.plugins”An array of plugin paths to load custom lint rules:
plugins: ["./lint-rules/org-standards.ts"]Config Resolution
Section titled “Config Resolution”- Load
chant.config.tsfrom the project root - Resolve and merge all
extendspresets (in order) - Apply local
ruleson top - Load
plugins - At lint time, apply
overridesto matching files