Skip to content

Kubernetes Concepts

llms.txt lists every page for an agent

Every exported resource declaration becomes a Kubernetes manifest document in the generated YAML. The serializer:

  • Resolves the correct apiVersion and kind from the resource type
  • Converts the export name to a kebab-case metadata.name
  • Nests user properties under spec (or at the top level for specless types like ConfigMap)
  • Merges default labels and annotations from defaultLabels()/defaultAnnotations()

Every Kubernetes resource has four standard fields:

FieldSourceExample
apiVersionResolved from resource typeapps/v1
kindResolved from resource typeDeployment
metadataFrom metadata property{ name: "my-app", labels: {...} }
specFrom spec property or remaining propsResource-specific configuration

K8s resources are organized by API group:

GroupapiVersionResources
Corev1Pod, Service, ConfigMap, Secret, Namespace, ServiceAccount
Appsapps/v1Deployment, StatefulSet, DaemonSet, ReplicaSet
Batchbatch/v1Job, CronJob
Networkingnetworking.k8s.io/v1Ingress, NetworkPolicy
RBACrbac.authorization.k8s.io/v1Role, ClusterRole, RoleBinding, ClusterRoleBinding
Autoscalingautoscaling/v2HorizontalPodAutoscaler
Policypolicy/v1PodDisruptionBudget

Nested objects like containers, probes, and volumes are expressed as property types:

import { Deployment, Container, Probe, ResourceRequirements } from "@intentius/chant-lexicon-k8s";
export const app = new Deployment({
metadata: { name: "my-app" },
spec: {
replicas: 2,
selector: { matchLabels: { app: "my-app" } },
template: {
metadata: { labels: { app: "my-app" } },
spec: {
containers: [
new Container({
name: "app",
image: "my-app:1.0",
resources: new ResourceRequirements({
limits: { cpu: "500m", memory: "256Mi" },
requests: { cpu: "100m", memory: "128Mi" },
}),
livenessProbe: new Probe({
httpGet: { path: "/healthz", port: 8080 },
initialDelaySeconds: 10,
}),
}),
],
},
},
},
});

Those property types are classes. Every other object a resource holds, such as DeploymentSpec, ServiceSpec or JobSpec, is an interface in the generated declarations, and you write it as an object literal. In @intentius/chant-lexicon-k8s/types, both check the keys and value types you pass, so spec: { replicas: "three" } is a type error there.

Some K8s resources (ConfigMap, Secret, Namespace, ServiceAccount) don’t have a spec field. Their data goes directly on the manifest:

import { ConfigMap, Secret } from "@intentius/chant-lexicon-k8s";
export const config = new ConfigMap({
metadata: { name: "app-config" },
data: { DATABASE_URL: "postgres://localhost:5432/mydb" },
});
export const secret = new Secret({
metadata: { name: "app-secret" },
stringData: { API_KEY: "changeme" },
});

Use defaultLabels() and defaultAnnotations() to inject metadata into all resources:

import { defaultLabels } from "@intentius/chant-lexicon-k8s";
export const labels = defaultLabels({
"app.kubernetes.io/managed-by": "chant",
"app.kubernetes.io/part-of": "my-system",
});

Explicit labels on individual resources take precedence over defaults.

Tip: Avoid hardcoding metadata.namespace — the WK8001 lint rule will flag it. Use a config variable or pass namespace at deploy time instead.

Inside a workspace, the k8s lexicon adds two variables to the env of every container of a Deployment, StatefulSet, DaemonSet, ReplicaSet, Job, CronJob or Pod, so that its spans name the declaration that produced them (#3059, ws-060).

containers:
- name: api
image: nginx:1.27
env:
- name: OTEL_SERVICE_NAME
value: api
- name: CHANT_RELEASE_ATTRIBUTES
valueFrom:
fieldRef:
fieldPath: metadata.annotations['chant.intentius.io/release-attributes']
- name: OTEL_RESOURCE_ATTRIBUTES
value: chant.workspace=acme,chant.member=svc,chant.decl=api$(CHANT_RELEASE_ATTRIBUTES)

OTEL_SERVICE_NAME is the workload’s metadata.name. OTEL_RESOURCE_ATTRIBUTES lists chant.workspace, chant.member, chant.decl (the declaration’s export name, which is its node id in chant workspace graph without the member prefix) and, when the build has an environment from --env or ownership.env, deployment.environment.name. A container whose image is pinned by digest (image@sha256:...) also gets service.version. A variable the container already sets is kept, and only the missing attributes are added; one set through valueFrom is left alone. Init containers are not stamped.

The git revision and the release’s digest are set by the deploy, not the build (ws-081). OTEL_RESOURCE_ATTRIBUTES ends with $(CHANT_RELEASE_ATTRIBUTES), which reads the pod annotation chant.intentius.io/release-attributes. A kubectl-apply step in chant run --components sets that annotation on the pod template of each stamped workload in what it applies, adding service.version and vcs.ref.head.revision. Any other apply leaves it absent, which reads as empty, and the manifest does not change with a release.

A project with no chant.workspace.json is unchanged. It can opt in with telemetry.attribution: true in chant.config.ts, which stamps the attributes that need no workspace, and inside a workspace telemetry.attribution: false turns it off.

chant