Skip to content

Component Contract

A component is declarative data. Its deploy is a composition of capabilities, not a script. The authoring form is TypeScript; it projects to a portable JSON contract that a non-chant or legacy component can hand-write and the same orchestrator can drive.

interface Component {
name: string;
dependsOn: string[]; // ordering only — no logic
build?: BuildSpec; // optional: source → BuildArchive (image/zip/jar, held in the archive)
deploy: Phase[]; // the component's own composition over shared capabilities
// verify + rollback fall out of the capabilities used
liveNames?: string[]; // optional: live entity/resource name(s), when they differ from `name`
}

build is optional: a config-only component has none. deploy is a composition using the same grammar as Ops — phases, parallel, gate, onFailure, dependsOn. See Composition & Wiring.

One component as four phases — build, publish, apply, verify — annotated with the three archetypes: service, infra, and producer/library. One component as four phases — build, publish, apply, verify — annotated with the three archetypes: service, infra, and producer/library.
A component's phases, and the three archetypes that use different subsets of them.

Because deploy is a composition, build-only and apply-only shapes are first-class, not special cases.

ArchetypePhasesExample
Servicebuild → publish → apply → verifyAn ECS service behind an ALB
Infraapply → verify (no build)A DynamoDB table, an EMR cluster, RDS/OpenSearch
Producer / librarybuild → publish (no service apply)A Java/JAR build published to S3 for EMR

A producer emits an artifact other components consume; its deploy is a single Publish phase. See cross-component wiring.

An ALB/ECS target: build an image, promote it at deploy time, apply the stack that registers the service on a shared ALB, wait for steady state. The shared-ALB values arrive as cross-stack outputs, resolved by the graph rather than by a describe-stacks shell script.

import { phase, type Component } from "@intentius/chant/components/component";
import { dockerBuild, publishImage, cfnDeploy, ecsUpdateService, waitSteadyState } from "@intentius/chant/components";
export const searchService: Component = {
name: "search-service",
dependsOn: ["shared-alb"],
build: dockerBuild({ context: ".", into: "archive" }),
deploy: [
phase("Publish", [publishImage({ from: "archive", to: "$env.registry" })]), // → digest
phase("Apply", [
cfnDeploy({ template: "archive:search.template.json",
inputs: { ...sharedAlb.outputs, image: "@Publish.digest" } }),
ecsUpdateService({ cluster: "$env.cluster", service: "search" }),
]),
phase("Verify", [waitSteadyState({ service: "search" })]),
],
};

The authoring form projects to a portable, versioned JSON contract. This is the substrate: the same document can be hand-written for a legacy component and driven by the same orchestrator.

{
"name": "search-service",
"dependsOn": ["shared-alb"],
"build": { "kind": "docker-build", "context": ".", "into": "archive" },
"deploy": [
{ "phase": "Publish", "steps": [{ "kind": "publish-image", "from": "archive", "to": "$env.registry" }] },
{ "phase": "Apply", "steps": [
{ "kind": "cfn-deploy", "template": "archive:search.template.json", "imageRef": "@Publish.digest" },
{ "kind": "ecs-update-service", "cluster": "$env.cluster", "service": "search" } ] },
{ "phase": "Verify", "steps": [ { "kind": "wait-steady-state", "service": "search" } ] }
]
}

A component is discovered the same way chant already discovers resources — by convention, from source, with no registration step. Export a Component (any export name) from a *.component.ts file anywhere under the project (excluding node_modules and a nested project’s own chant.config.ts scope) and chant finds it. archetype does not have to be authored explicitly: when omitted, chant infers it from the composition’s shape (a build present or absent, and whether every step in deploy is publish-family) — the same inference projectToJson applies before validating against the schema.

search-service.component.ts
export const searchService: Component = {
name: "search-service",
// archetype omitted — inferred as "service" (build + a non-publish Apply step)
dependsOn: ["shared-alb"],
build: dockerBuild({ context: ".", into: "archive" }),
deploy: [ /* … */ ],
};
Terminal window
chant list --components # inventory: name, archetype, dependsOn, phases
chant describe search-service --components # one component's full JSON projection
chant graph --components # dependency order + parallel-safe waves