@intentius/chant-lexicon-k8s 0.44.13 → 0.45.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/dist/codegen/docs.d.ts.map +1 -1
  2. package/dist/codegen/generate-lexicon.d.ts +15 -1
  3. package/dist/codegen/generate-lexicon.d.ts.map +1 -1
  4. package/dist/components/capability-plugin.d.ts.map +1 -1
  5. package/dist/crd/parser.d.ts +28 -1
  6. package/dist/crd/parser.d.ts.map +1 -1
  7. package/dist/deep-observe-hooks.d.ts +60 -4
  8. package/dist/deep-observe-hooks.d.ts.map +1 -1
  9. package/dist/deep-observe.d.ts +36 -61
  10. package/dist/deep-observe.d.ts.map +1 -1
  11. package/dist/integrity.json +6 -3
  12. package/dist/lint/audit-catalog.d.ts.map +1 -1
  13. package/dist/lint/post-synth/crd-schema-helpers.d.ts +49 -0
  14. package/dist/lint/post-synth/crd-schema-helpers.d.ts.map +1 -0
  15. package/dist/lint/post-synth/index.d.ts.map +1 -1
  16. package/dist/lint/post-synth/wk8501.d.ts +14 -0
  17. package/dist/lint/post-synth/wk8501.d.ts.map +1 -0
  18. package/dist/lint/post-synth/wk8502.d.ts +13 -0
  19. package/dist/lint/post-synth/wk8502.d.ts.map +1 -0
  20. package/dist/manifest.json +1 -1
  21. package/dist/meta.json +126 -63
  22. package/dist/okf/index.md +2 -0
  23. package/dist/okf/rules/WK8501.md +11 -0
  24. package/dist/okf/rules/WK8502.md +11 -0
  25. package/dist/rules/crd-schema-helpers.ts +228 -0
  26. package/dist/rules/wk8501.ts +40 -0
  27. package/dist/rules/wk8502.ts +39 -0
  28. package/dist/spec/parse.d.ts +32 -0
  29. package/dist/spec/parse.d.ts.map +1 -1
  30. package/dist/validate.d.ts.map +1 -1
  31. package/package.json +3 -3
  32. package/src/codegen/docs.ts +0 -1044
  33. package/src/codegen/generate-lexicon.ts +38 -2
  34. package/src/codegen/snapshot.test.ts +20 -0
  35. package/src/components/capability-plugin.test.ts +20 -0
  36. package/src/components/capability-plugin.ts +5 -2
  37. package/src/crd/cnpg.test.ts +1 -1
  38. package/src/crd/infisical.test.ts +1 -1
  39. package/src/crd/parser.test.ts +117 -1
  40. package/src/crd/parser.ts +71 -5
  41. package/src/crd/traefik.test.ts +1 -1
  42. package/src/deep-observe-hooks.ts +78 -16
  43. package/src/deep-observe.test.ts +145 -29
  44. package/src/deep-observe.ts +43 -85
  45. package/src/generated/lexicon-k8s.json +126 -63
  46. package/src/lint/audit-catalog.ts +2 -0
  47. package/src/lint/post-synth/crd-schema-helpers.ts +228 -0
  48. package/src/lint/post-synth/index.ts +4 -0
  49. package/src/lint/post-synth/post-synth.test.ts +156 -0
  50. package/src/lint/post-synth/wk8501.ts +40 -0
  51. package/src/lint/post-synth/wk8502.ts +39 -0
  52. package/src/list-map-key-table.test.ts +83 -0
  53. package/src/op/activities/kubectl.ts +2 -2
  54. package/src/serializer.test.ts +4 -4
  55. package/src/spec/parse.ts +33 -0
  56. package/src/validate.ts +7 -4
@@ -135,1051 +135,7 @@ export async function generateDocs(opts?: { verbose?: boolean }): Promise<void>
135
135
  outputFormat,
136
136
  serviceFromType,
137
137
  suppressPages: ["pseudo-parameters"],
138
- extraPages: [
139
- {
140
- slug: "getting-started",
141
- title: "Getting Started",
142
- description: "Install chant and deploy your first Kubernetes manifest in 5 minutes",
143
- content: `## What is chant?
144
-
145
- Chant is a TypeScript-to-YAML compiler for Kubernetes. You write typed TypeScript declarations, and chant outputs kubectl-ready manifests.
146
-
147
- ## Install
148
-
149
- \`\`\`bash
150
- npm install --save-dev @intentius/chant @intentius/chant-lexicon-k8s
151
- \`\`\`
152
-
153
- ## Your first deployment
154
-
155
- The fastest path is the **WebApp** composite — one function call that produces a Deployment, Service, and optional Ingress:
156
-
157
- \`\`\`typescript
158
- // src/infra.k8s.ts
159
- import { WebApp } from "@intentius/chant-lexicon-k8s";
160
-
161
- const app = WebApp({
162
- name: "hello",
163
- image: "nginx:1.25",
164
- port: 80,
165
- replicas: 2,
166
- });
167
-
168
- export const { deployment, service } = app;
169
- \`\`\`
170
-
171
- Build and deploy:
172
-
173
- \`\`\`bash
174
- # Generate YAML manifests
175
- chant build --output dist/manifests.yaml
176
-
177
- # Validate against the cluster API (no changes applied)
178
- kubectl apply -f dist/manifests.yaml --dry-run=server
179
-
180
- # Apply for real
181
- kubectl apply -f dist/manifests.yaml
182
- \`\`\`
183
-
184
- ## Using resource constructors
185
-
186
- Composites are convenient, but you can also use the lower-level resource constructors directly:
187
-
188
- \`\`\`typescript
189
- // src/infra.k8s.ts
190
- import { Deployment, Service, Container, Probe } from "@intentius/chant-lexicon-k8s";
191
-
192
- export const deployment = new Deployment({
193
- metadata: { name: "hello", labels: { "app.kubernetes.io/name": "hello" } },
194
- spec: {
195
- replicas: 2,
196
- selector: { matchLabels: { "app.kubernetes.io/name": "hello" } },
197
- template: {
198
- metadata: { labels: { "app.kubernetes.io/name": "hello" } },
199
- spec: {
200
- containers: [
201
- new Container({
202
- name: "web",
203
- image: "nginx:1.25",
204
- ports: [{ containerPort: 80, name: "http" }],
205
- livenessProbe: new Probe({ httpGet: { path: "/", port: 80 } }),
206
- readinessProbe: new Probe({ httpGet: { path: "/", port: 80 } }),
207
- }),
208
- ],
209
- },
210
- },
211
- },
212
- });
213
-
214
- export const service = new Service({
215
- metadata: { name: "hello" },
216
- spec: {
217
- selector: { "app.kubernetes.io/name": "hello" },
218
- ports: [{ port: 80, targetPort: 80, name: "http" }],
219
- },
220
- });
221
- \`\`\`
222
-
223
- Composites return plain prop objects. Resource constructors (\`new Deployment(...)\`) accept the same shape. Both produce identical YAML.
224
-
225
- ## Next steps
226
-
227
- - [Kubernetes Concepts](/chant/lexicons/k8s/kubernetes-concepts/) — how K8s resources map to chant constructs
228
- - [Examples: Composites](/chant/lexicons/k8s/composite-examples/) — WebApp, CronWorkload, AutoscaledService, and more
229
- - [Lint Rules](/chant/lexicons/k8s/lint-rules/) — built-in checks for security, reliability, and best practices`,
230
- },
231
- {
232
- slug: "kubernetes-concepts",
233
- title: "Kubernetes Concepts",
234
- description: "How Kubernetes resources map to chant constructs — apiVersion, kind, metadata, spec",
235
- content: `Every exported resource declaration becomes a Kubernetes manifest document in the generated YAML. The serializer handles the translation automatically:
236
-
237
- - Resolves the correct \`apiVersion\` and \`kind\` from the resource type
238
- - Converts the export name to a kebab-case \`metadata.name\`
239
- - Nests user properties under \`spec\` (or at the top level for specless types like ConfigMap)
240
- - Merges default labels and annotations from \`defaultLabels()\`/\`defaultAnnotations()\`
241
-
242
- ## Resource structure
243
-
244
- Every Kubernetes resource has four standard fields:
245
-
246
- | Field | Source | Example |
247
- |-------|--------|---------|
248
- | \`apiVersion\` | Resolved from resource type | \`apps/v1\` |
249
- | \`kind\` | Resolved from resource type | \`Deployment\` |
250
- | \`metadata\` | From \`metadata\` property | \`{ name: "my-app", labels: {...} }\` |
251
- | \`spec\` | From \`spec\` property or remaining props | Resource-specific configuration |
252
-
253
- ## API groups
254
-
255
- K8s resources are organized by API group:
256
-
257
- | Group | apiVersion | Resources |
258
- |-------|-----------|-----------|
259
- | Core | \`v1\` | Pod, Service, ConfigMap, Secret, Namespace, ServiceAccount |
260
- | Apps | \`apps/v1\` | Deployment, StatefulSet, DaemonSet, ReplicaSet |
261
- | Batch | \`batch/v1\` | Job, CronJob |
262
- | Networking | \`networking.k8s.io/v1\` | Ingress, NetworkPolicy |
263
- | RBAC | \`rbac.authorization.k8s.io/v1\` | Role, ClusterRole, RoleBinding, ClusterRoleBinding |
264
- | Autoscaling | \`autoscaling/v2\` | HorizontalPodAutoscaler |
265
- | Policy | \`policy/v1\` | PodDisruptionBudget |
266
-
267
- ## Property types
268
-
269
- Nested objects like containers, probes, and volumes are expressed as property types:
270
-
271
- \`\`\`typescript
272
- import { Deployment, Container, Probe, ResourceRequirements } from "@intentius/chant-lexicon-k8s";
273
-
274
- export const app = new Deployment({
275
- metadata: { name: "my-app" },
276
- spec: {
277
- replicas: 2,
278
- selector: { matchLabels: { app: "my-app" } },
279
- template: {
280
- metadata: { labels: { app: "my-app" } },
281
- spec: {
282
- containers: [
283
- new Container({
284
- name: "app",
285
- image: "my-app:1.0",
286
- resources: new ResourceRequirements({
287
- limits: { cpu: "500m", memory: "256Mi" },
288
- requests: { cpu: "100m", memory: "128Mi" },
289
- }),
290
- livenessProbe: new Probe({
291
- httpGet: { path: "/healthz", port: 8080 },
292
- initialDelaySeconds: 10,
293
- }),
294
- }),
295
- ],
296
- },
297
- },
298
- },
299
- });
300
- \`\`\`
301
-
302
- ## Specless types
303
-
304
- Some K8s resources (ConfigMap, Secret, Namespace, ServiceAccount) don't have a \`spec\` field. Their data goes directly on the manifest:
305
-
306
- \`\`\`typescript
307
- import { ConfigMap, Secret } from "@intentius/chant-lexicon-k8s";
308
-
309
- export const config = new ConfigMap({
310
- metadata: { name: "app-config" },
311
- data: { DATABASE_URL: "postgres://localhost:5432/mydb" },
312
- });
313
-
314
- export const secret = new Secret({
315
- metadata: { name: "app-secret" },
316
- stringData: { API_KEY: "changeme" },
317
- });
318
- \`\`\`
319
-
320
- ## Default labels and annotations
321
-
322
- Use \`defaultLabels()\` and \`defaultAnnotations()\` to inject metadata into all resources:
323
-
324
- \`\`\`typescript
325
- import { defaultLabels } from "@intentius/chant-lexicon-k8s";
326
-
327
- export const labels = defaultLabels({
328
- "app.kubernetes.io/managed-by": "chant",
329
- "app.kubernetes.io/part-of": "my-system",
330
- });
331
- \`\`\`
332
-
333
- Explicit labels on individual resources take precedence over defaults.
334
-
335
- > **Tip:** Avoid hardcoding \`metadata.namespace\` — the [WK8001 lint rule](/chant/lexicons/k8s/lint-rules/) will flag it. Use a config variable or pass namespace at deploy time instead.`,
336
- },
337
- {
338
- slug: "lint-rules",
339
- title: "Lint Rules",
340
- description: "Built-in lint rules and post-synth checks for Kubernetes manifests",
341
- content: `The Kubernetes lexicon ships lint rules that run during \`chant lint\` and post-synth checks that validate the serialized YAML after \`chant build\`.
342
-
343
- ## Lint rules
344
-
345
- Lint rules analyze your TypeScript source code before build.
346
-
347
- ### WK8001 — Hardcoded namespace
348
-
349
- **Severity:** warning | **Category:** correctness
350
-
351
- Flags hardcoded namespace strings in resource constructors. Namespaces should be parameterized or derived from configuration.
352
-
353
- \`\`\`typescript
354
- // Bad — hardcoded namespace
355
- new Deployment({ metadata: { namespace: "production" } });
356
-
357
- // Good — parameterized
358
- new Deployment({ metadata: { namespace: config.namespace } });
359
- \`\`\`
360
-
361
- ## Post-synth checks
362
-
363
- Post-synth checks run against the serialized YAML after build.
364
-
365
- ### Security
366
-
367
- | Rule | Description |
368
- |------|-------------|
369
- | WK8005 | Hardcoded secrets in environment variables |
370
- | WK8041 | API keys detected in env values |
371
- | WK8042 | Private keys in ConfigMaps or Secrets |
372
- | WK8202 | Privileged container (\`privileged: true\`) |
373
- | WK8203 | Writable root filesystem (\`readOnlyRootFilesystem\` not set) |
374
- | WK8204 | Container running as root (\`runAsNonRoot\` not set) |
375
- | WK8205 | Capabilities not dropped (\`drop: ["ALL"]\` missing) |
376
- | WK8207 | Host network access (\`hostNetwork: true\`) |
377
- | WK8208 | Host PID namespace (\`hostPID: true\`) |
378
- | WK8209 | Host IPC namespace (\`hostIPC: true\`) |
379
-
380
- ### Best practices
381
-
382
- | Rule | Description |
383
- |------|-------------|
384
- | WK8006 | Latest image tag or untagged image |
385
- | WK8101 | Deployment selector doesn't match template labels |
386
- | WK8102 | Resource missing metadata.labels |
387
- | WK8103 | Container missing name |
388
- | WK8104 | Unnamed container ports |
389
- | WK8105 | Missing imagePullPolicy |
390
-
391
- ### Reliability
392
-
393
- | Rule | Description |
394
- |------|-------------|
395
- | WK8201 | Container missing resource limits |
396
- | WK8301 | Port-serving container missing health probes (skips Jobs/CronJobs and port-less workers) |
397
- | WK8302 | Single replica Deployment |
398
- | WK8303 | HA Deployment without PodDisruptionBudget |
399
-
400
- ### Argo CD
401
-
402
- Quality checks for the [Argo CD composites](/chant/lexicons/k8s/argo-composites/). ARGO001/ARGO004 are declarative (source AST); ARGO002/003/005 are post-synth (cross-resource / filesystem).
403
-
404
- | Rule | Description |
405
- |------|-------------|
406
- | ARGO001 | Production \`Application\` enables automated \`prune\` without the \`argocd.chant.dev/allow-prune\` override |
407
- | ARGO002 | \`Application.spec.project\` references an undeclared \`AppProject\` |
408
- | ARGO003 | \`Application.spec.destination\` references an unregistered cluster |
409
- | ARGO004 | \`ApplicationSet\` template doesn't scope to a single static \`AppProject\` |
410
- | ARGO005 | \`Application\` \`source.path\` doesn't resolve to a directory (warn) |
411
-
412
- ### Flux
413
-
414
- Quality checks for the [Flux composites](/chant/lexicons/k8s/flux-composites/) (\`FluxGitSource\` / \`FluxAppFor\`). FLUX001 is declarative (source AST); FLUX002/003 are post-synth (cross-resource).
415
-
416
- | Rule | Description |
417
- |------|-------------|
418
- | FLUX001 | \`GitRepository\` has no \`spec.ref\` pin — Flux falls back to the \`master\` branch |
419
- | FLUX002 | \`Kustomization.spec.sourceRef\` references an undeclared source (the bootstrap \`flux-system\` repo is exempt) |
420
- | FLUX003 | \`Kustomization.spec.dependsOn\` names a \`Kustomization\` nothing in the build declares, or itself (warn) |
421
-
422
- ## Running lint
423
-
424
- \`\`\`bash
425
- # Lint your chant project
426
- chant lint
427
-
428
- # Build (also runs post-synth checks)
429
- chant build
430
- \`\`\`
431
-
432
- To suppress a rule on a specific line:
433
-
434
- \`\`\`typescript
435
- // chant-disable-next-line WK8001
436
- export const deploy = new Deployment({ metadata: { namespace: "prod" } });
437
- \`\`\`
438
-
439
- To suppress globally in \`chant.config.ts\`:
440
-
441
- \`\`\`typescript
442
- export default {
443
- lint: {
444
- rules: {
445
- WK8001: "off",
446
- },
447
- },
448
- };
449
- \`\`\`
450
- `,
451
- },
452
- {
453
- slug: "examples",
454
- title: "Examples: Resources",
455
- description: "Walkthrough of Kubernetes resource examples — deployments, services, autoscaling",
456
- content: `## Basic Deployment
457
-
458
- A Deployment with Service — the most common pattern for stateless web apps:
459
-
460
- \`\`\`typescript
461
- import { Deployment, Service, Container, Probe } from "@intentius/chant-lexicon-k8s";
462
-
463
- export const deployment = new Deployment({
464
- metadata: { name: "web", labels: { "app.kubernetes.io/name": "web" } },
465
- spec: {
466
- replicas: 3,
467
- selector: { matchLabels: { "app.kubernetes.io/name": "web" } },
468
- template: {
469
- metadata: { labels: { "app.kubernetes.io/name": "web" } },
470
- spec: {
471
- containers: [
472
- new Container({
473
- name: "web",
474
- image: "nginx:1.25",
475
- ports: [{ containerPort: 80, name: "http" }],
476
- livenessProbe: new Probe({ httpGet: { path: "/", port: 80 } }),
477
- readinessProbe: new Probe({ httpGet: { path: "/", port: 80 } }),
478
- }),
479
- ],
480
- },
481
- },
482
- },
483
- });
484
-
485
- export const service = new Service({
486
- metadata: { name: "web" },
487
- spec: {
488
- selector: { "app.kubernetes.io/name": "web" },
489
- ports: [{ port: 80, targetPort: 80 }],
490
- },
491
- });
492
- \`\`\`
493
-
494
- ## Microservice with HPA
495
-
496
- Production-ready microservice with autoscaling and disruption budget:
497
-
498
- \`\`\`typescript
499
- import {
500
- Deployment, Service, HorizontalPodAutoscaler, PodDisruptionBudget,
501
- Container, Probe, ResourceRequirements,
502
- } from "@intentius/chant-lexicon-k8s";
503
-
504
- export const deployment = new Deployment({
505
- metadata: { name: "api", labels: { "app.kubernetes.io/name": "api" } },
506
- spec: {
507
- replicas: 2,
508
- selector: { matchLabels: { "app.kubernetes.io/name": "api" } },
509
- template: {
510
- metadata: { labels: { "app.kubernetes.io/name": "api" } },
511
- spec: {
512
- containers: [
513
- new Container({
514
- name: "api",
515
- image: "api:1.0",
516
- ports: [{ containerPort: 8080, name: "http" }],
517
- resources: new ResourceRequirements({
518
- limits: { cpu: "500m", memory: "256Mi" },
519
- requests: { cpu: "100m", memory: "128Mi" },
520
- }),
521
- livenessProbe: new Probe({ httpGet: { path: "/healthz", port: 8080 } }),
522
- readinessProbe: new Probe({ httpGet: { path: "/readyz", port: 8080 } }),
523
- }),
524
- ],
525
- },
526
- },
527
- },
528
- });
529
-
530
- export const hpa = new HorizontalPodAutoscaler({
531
- metadata: { name: "api" },
532
- spec: {
533
- scaleTargetRef: { apiVersion: "apps/v1", kind: "Deployment", name: "api" },
534
- minReplicas: 2,
535
- maxReplicas: 10,
536
- metrics: [
537
- { type: "Resource", resource: { name: "cpu", target: { type: "Utilization", averageUtilization: 70 } } },
538
- ],
539
- },
540
- });
541
-
542
- export const pdb = new PodDisruptionBudget({
543
- metadata: { name: "api" },
544
- spec: {
545
- minAvailable: 1,
546
- selector: { matchLabels: { "app.kubernetes.io/name": "api" } },
547
- },
548
- });
549
- \`\`\`
550
-
551
- ## Stateful Application
552
-
553
- Database deployment with persistent storage using the StatefulApp composite:
554
-
555
- \`\`\`typescript
556
- import { StatefulApp } from "@intentius/chant-lexicon-k8s";
557
-
558
- const { statefulSet, service } = StatefulApp({
559
- name: "postgres",
560
- image: "postgres:16",
561
- port: 5432,
562
- storageSize: "20Gi",
563
- replicas: 3,
564
- env: [{ name: "POSTGRES_DB", value: "mydb" }],
565
- });
566
- \`\`\`
567
- `,
568
- },
569
- {
570
- slug: "composite-examples",
571
- title: "Examples: Composites",
572
- description: "Composite examples — WebApp, CronWorkload, AutoscaledService, WorkerPool, NamespaceEnv, NodeAgent",
573
- content: `import Diagram from '../../components/Diagram.astro';
574
-
575
- Composites are higher-level constructs that produce multiple coordinated K8s resources from a single function call.
576
-
577
- <Diagram name="composite-expansion" alt="Single CockroachDbCluster composite declaration expanding into Namespace, StatefulSet, Service, ServiceAccount, NetworkPolicy, PodDisruptionBudget, and ConfigMap resources serialized to one YAML file" caption="Composite expansion: one declaration → many resources" />
578
-
579
- ## WebApp
580
-
581
- Quick deployment with Deployment + Service + optional Ingress:
582
-
583
- \`\`\`typescript
584
- import { WebApp } from "@intentius/chant-lexicon-k8s";
585
-
586
- const { deployment, service, ingress } = WebApp({
587
- name: "frontend",
588
- image: "frontend:1.0",
589
- port: 3000,
590
- replicas: 3,
591
- ingressHost: "frontend.example.com",
592
- ingressTlsSecret: "frontend-tls",
593
- });
594
- \`\`\`
595
-
596
- ## CronWorkload
597
-
598
- Scheduled workload with RBAC:
599
-
600
- \`\`\`typescript
601
- import { CronWorkload } from "@intentius/chant-lexicon-k8s";
602
-
603
- const { cronJob, serviceAccount, role, roleBinding } = CronWorkload({
604
- name: "db-backup",
605
- image: "postgres:16",
606
- schedule: "0 2 * * *",
607
- command: ["pg_dump", "-h", "postgres", "mydb"],
608
- rbacRules: [
609
- { apiGroups: [""], resources: ["secrets"], verbs: ["get"] },
610
- ],
611
- });
612
- \`\`\`
613
-
614
- ## AutoscaledService
615
-
616
- Production HTTP service with HPA, PDB, and configurable probes:
617
-
618
- \`\`\`typescript
619
- import { AutoscaledService } from "@intentius/chant-lexicon-k8s";
620
-
621
- const { deployment, service, hpa, pdb } = AutoscaledService({
622
- name: "api",
623
- image: "api:2.0",
624
- port: 8080,
625
- maxReplicas: 10,
626
- minReplicas: 3,
627
- targetCPUPercent: 60,
628
- targetMemoryPercent: 80,
629
- cpuRequest: "200m",
630
- memoryRequest: "256Mi",
631
- cpuLimit: "1",
632
- memoryLimit: "1Gi",
633
- livenessPath: "/healthz",
634
- readinessPath: "/readyz",
635
- topologySpread: true,
636
- namespace: "production",
637
- });
638
- \`\`\`
639
-
640
- ## WorkerPool
641
-
642
- Background queue worker with RBAC and optional autoscaling:
643
-
644
- \`\`\`typescript
645
- import { WorkerPool } from "@intentius/chant-lexicon-k8s";
646
-
647
- const { deployment, serviceAccount, role, roleBinding, configMap, hpa } = WorkerPool({
648
- name: "email-worker",
649
- image: "worker:1.0",
650
- command: ["bundle", "exec", "sidekiq"],
651
- config: { REDIS_URL: "redis://redis:6379", QUEUE: "emails" },
652
- autoscaling: { minReplicas: 2, maxReplicas: 20, targetCPUPercent: 60 },
653
- });
654
- \`\`\`
655
-
656
- Pass \`rbacRules: []\` to opt out of RBAC resource creation entirely.
657
-
658
- ## NamespaceEnv
659
-
660
- Multi-tenant namespace with resource guardrails and network isolation:
661
-
662
- \`\`\`typescript
663
- import { NamespaceEnv } from "@intentius/chant-lexicon-k8s";
664
-
665
- const { namespace, resourceQuota, limitRange, networkPolicy } = NamespaceEnv({
666
- name: "team-alpha",
667
- cpuQuota: "8",
668
- memoryQuota: "16Gi",
669
- maxPods: 50,
670
- defaultCpuRequest: "100m",
671
- defaultMemoryRequest: "128Mi",
672
- defaultCpuLimit: "500m",
673
- defaultMemoryLimit: "512Mi",
674
- defaultDenyIngress: true,
675
- defaultDenyEgress: true,
676
- });
677
- \`\`\`
678
-
679
- Setting a ResourceQuota without LimitRange defaults will emit a warning — pods without explicit resource requests will fail to schedule.
680
-
681
- ## NodeAgent
682
-
683
- Per-node DaemonSet agent with cluster-wide RBAC and host path mounts:
684
-
685
- \`\`\`typescript
686
- import { NodeAgent } from "@intentius/chant-lexicon-k8s";
687
-
688
- const { daemonSet, serviceAccount, clusterRole, clusterRoleBinding, configMap } = NodeAgent({
689
- name: "log-collector",
690
- image: "fluentd:v1.16",
691
- port: 24224,
692
- hostPaths: [
693
- { name: "varlog", hostPath: "/var/log", mountPath: "/var/log" },
694
- { name: "containers", hostPath: "/var/lib/docker/containers", mountPath: "/var/lib/docker/containers" },
695
- ],
696
- config: { "fluent.conf": "<source>\\n @type tail\\n path /var/log/*.log\\n</source>" },
697
- rbacRules: [
698
- { apiGroups: [""], resources: ["pods", "namespaces"], verbs: ["get", "list", "watch"] },
699
- ],
700
- namespace: "monitoring",
701
- });
702
- \`\`\`
703
-
704
- ## Deploying composites
705
-
706
- Composites produce plain prop objects. To deploy them, write each resource to a \`.k8s.ts\` file then use the standard chant build → kubectl apply workflow.
707
-
708
- ### Step 1 — Write a chant source file
709
-
710
- \`\`\`typescript
711
- // src/infra.k8s.ts
712
- import { AutoscaledService, NamespaceEnv, WorkerPool, NodeAgent } from "@intentius/chant-lexicon-k8s";
713
-
714
- // Namespace with guardrails
715
- const nsEnv = NamespaceEnv({
716
- name: "production",
717
- cpuQuota: "16",
718
- memoryQuota: "32Gi",
719
- defaultCpuRequest: "100m",
720
- defaultMemoryRequest: "128Mi",
721
- defaultCpuLimit: "1",
722
- defaultMemoryLimit: "512Mi",
723
- });
724
- export const { namespace, resourceQuota, limitRange, networkPolicy } = nsEnv;
725
-
726
- // API with autoscaling
727
- const api = AutoscaledService({
728
- name: "api",
729
- image: "api:1.0",
730
- port: 8080,
731
- maxReplicas: 10,
732
- cpuRequest: "200m",
733
- memoryRequest: "256Mi",
734
- topologySpread: true,
735
- namespace: "production",
736
- });
737
- export const { deployment, service, hpa, pdb } = api;
738
-
739
- // Background workers
740
- const workers = WorkerPool({
741
- name: "email-worker",
742
- image: "worker:1.0",
743
- command: ["bundle", "exec", "sidekiq"],
744
- config: { REDIS_URL: "redis://redis:6379" },
745
- autoscaling: { minReplicas: 2, maxReplicas: 20 },
746
- namespace: "production",
747
- });
748
- export const workerDeployment = workers.deployment;
749
- export const workerSA = workers.serviceAccount;
750
- export const workerRole = workers.role;
751
- export const workerRoleBinding = workers.roleBinding;
752
- export const workerConfig = workers.configMap;
753
- export const workerHPA = workers.hpa;
754
- \`\`\`
755
-
756
- ### Step 2 — Build and validate
757
-
758
- \`\`\`bash
759
- # Build YAML manifests
760
- chant build src/ --output manifests.yaml
761
-
762
- # Lint for common issues
763
- chant lint src/
764
-
765
- # Server-side dry run (validates with admission webhooks)
766
- kubectl apply -f manifests.yaml --dry-run=server
767
- \`\`\`
768
-
769
- ### Step 3 — Deploy
770
-
771
- \`\`\`bash
772
- # Diff before applying
773
- kubectl diff -f manifests.yaml
774
-
775
- # Apply
776
- kubectl apply -f manifests.yaml
777
-
778
- # Verify rollout
779
- kubectl rollout status deployment/api -n production
780
- kubectl get pods,svc,hpa -n production
781
- \`\`\`
782
-
783
- ### k3d local validation
784
-
785
- For local testing before pushing to a real cluster:
786
-
787
- \`\`\`bash
788
- cd lexicons/k8s
789
-
790
- # Create a k3d cluster, apply composites, verify, and delete
791
- just k3d-validate-composites
792
-
793
- # Keep the cluster for manual inspection
794
- just k3d-validate-composites --keep-cluster
795
-
796
- # Reuse an existing cluster
797
- just k3d-validate-composites --reuse-cluster --verbose
798
- \`\`\`
799
-
800
- The \`/chant-k8s\` AI skill covers the full lifecycle — scaffold, build, lint, apply, rollback, and troubleshooting.
801
- `,
802
- },
803
- {
804
- slug: "operational-playbook",
805
- title: "Operational Playbook",
806
- description: "Build, deploy, debug, and troubleshoot Kubernetes manifests produced by chant",
807
- content: `This playbook covers the full lifecycle of chant-produced Kubernetes manifests \u2014 from build through production debugging. The same content is available to AI agents via the \`/chant-k8s\` skill.
808
-
809
- ## Build & validate
810
-
811
- | Step | Command | What it catches |
812
- |------|---------|-----------------|
813
- | Lint source | \`chant lint src/\` | Hardcoded namespaces (WK8001) |
814
- | Build manifests | \`chant build src/ --output manifests.yaml\` | Post-synth: secrets in env (WK8005), latest tags (WK8006), API keys (WK8041), missing probes (WK8301), no resource limits (WK8201), privileged containers (WK8202), and more |
815
- | Server dry-run | \`kubectl apply -f manifests.yaml --dry-run=server\` | K8s API validation: schema errors, admission webhooks |
816
-
817
- Run lint on every edit. Run build + dry-run before every apply.
818
-
819
- ## Deploy to Kubernetes
820
-
821
- \`\`\`bash
822
- # Build
823
- chant build src/ --output manifests.yaml
824
-
825
- # Diff before applying
826
- kubectl diff -f manifests.yaml
827
-
828
- # Dry run (validates with admission webhooks)
829
- kubectl apply -f manifests.yaml --dry-run=server
830
-
831
- # Apply
832
- kubectl apply -f manifests.yaml
833
- \`\`\`
834
-
835
- ## Rollout & rollback
836
-
837
- \`\`\`bash
838
- # Watch rollout progress
839
- kubectl rollout status deployment/my-app --timeout=300s
840
-
841
- # Check rollout history
842
- kubectl rollout history deployment/my-app
843
-
844
- # Undo last rollout
845
- kubectl rollout undo deployment/my-app
846
-
847
- # Roll back to a specific revision
848
- kubectl rollout undo deployment/my-app --to-revision=2
849
- \`\`\`
850
-
851
- ## Debugging strategies
852
-
853
- ### Pod status and events
854
-
855
- \`\`\`bash
856
- # Overview
857
- kubectl get pods -l app.kubernetes.io/name=my-app
858
- kubectl get events --sort-by=.lastTimestamp -n <namespace>
859
-
860
- # Deep dive into a specific pod
861
- kubectl describe pod <pod-name>
862
-
863
- # Logs (current and previous crash)
864
- kubectl logs <pod-name>
865
- kubectl logs <pod-name> --previous
866
- kubectl logs <pod-name> -c <container-name> # specific container
867
- kubectl logs deployment/my-app --all-containers
868
-
869
- # Debug containers (K8s 1.25+)
870
- kubectl debug <pod-name> -it --image=busybox --target=<container>
871
-
872
- # Port-forwarding for local testing
873
- kubectl port-forward svc/my-app 8080:80
874
- kubectl port-forward pod/<pod-name> 8080:8080
875
- \`\`\`
876
-
877
- ### Resource inspection
878
-
879
- \`\`\`bash
880
- # Get all resources in namespace
881
- kubectl get all -n <namespace>
882
-
883
- # YAML output for debugging
884
- kubectl get deployment/my-app -o yaml
885
-
886
- # Check resource usage
887
- kubectl top pods -l app.kubernetes.io/name=my-app
888
- kubectl top nodes
889
- \`\`\`
890
-
891
- ## Common error patterns
892
-
893
- | Status | Meaning | Diagnostic command | Typical fix |
894
- |--------|---------|-------------------|-------------|
895
- | Pending | Not scheduled | \`kubectl describe pod\` \u2192 Events | Check resource requests, node selectors, taints, PVC binding |
896
- | CrashLoopBackOff | App crashing on start | \`kubectl logs --previous\` | Fix app startup, check probe config, increase initialDelaySeconds |
897
- | ImagePullBackOff | Image not found | \`kubectl describe pod\` \u2192 Events | Verify image name/tag, check imagePullSecrets, registry auth |
898
- | OOMKilled | Out of memory | \`kubectl describe pod\` \u2192 Last State | Increase memory limit, profile app memory usage |
899
- | Evicted | Node disk/memory pressure | \`kubectl describe node\` | Increase limits, add node capacity, check for log/tmp bloat |
900
- | CreateContainerError | Container config issue | \`kubectl describe pod\` \u2192 Events | Check volume mounts, configmap/secret refs, security context |
901
- | Init:CrashLoopBackOff | Init container failing | \`kubectl logs -c <init-container>\` | Fix init container command, check dependencies |
902
-
903
- ## Deployment strategies
904
-
905
- - **RollingUpdate** (default): Gradually replaces pods. Set \`maxSurge\` and \`maxUnavailable\`.
906
- - **Recreate**: All pods terminated before new ones created. Use for stateful apps that cannot run multiple versions.
907
- - **Canary**: Deploy a second Deployment with 1 replica + same selector labels. Route percentage via Ingress annotations or service mesh.
908
- - **Blue/Green**: Two full Deployments (blue/green), switch Service selector between them.
909
-
910
- ## Production safety
911
-
912
- ### Pre-apply validation
913
-
914
- \`\`\`bash
915
- # Always diff before applying
916
- kubectl diff -f manifests.yaml
917
-
918
- # Server-side dry run (validates with admission webhooks)
919
- kubectl apply -f manifests.yaml --dry-run=server
920
-
921
- # Client-side dry run (fast, but no webhook validation)
922
- kubectl apply -f manifests.yaml --dry-run=client
923
- \`\`\`
924
-
925
- Use server-side dry-run before production applies \u2014 it catches schema errors and runs admission webhooks. Client-side dry-run is faster but only validates locally.
926
-
927
- ## Troubleshooting reference
928
-
929
- | Symptom | Likely cause | Resolution |
930
- |---------|-------------|------------|
931
- | Pod stuck in Pending | Insufficient CPU/memory on nodes | Scale up cluster or reduce resource requests |
932
- | Pod stuck in Pending | PVC not bound | Check StorageClass exists, PV available |
933
- | Pod stuck in Pending | Node selector/affinity mismatch | Verify node labels match selectors |
934
- | Pod stuck in ContainerCreating | ConfigMap/Secret not found | Ensure referenced ConfigMaps/Secrets exist |
935
- | Pod stuck in ContainerCreating | Volume mount failure | Check PVC status, CSI driver health |
936
- | Service returns 503 | No ready endpoints | Check pod readiness probes, selector match |
937
- | Service returns 503 | Wrong port configuration | Verify targetPort matches containerPort |
938
- | Ingress returns 404 | Backend service not found | Check Ingress rules, service name/port |
939
- | Ingress returns 404 | Wrong path matching | Check pathType (Prefix vs Exact) |
940
- | HPA not scaling | Metrics server not installed | Install metrics-server |
941
- | HPA not scaling | Resource requests not set | Add CPU/memory requests to containers |
942
- | CronJob not running | Invalid cron expression | Validate cron syntax (5-field format) |
943
- | NetworkPolicy blocking | Default deny applied | Add explicit allow rules for required traffic |
944
- | RBAC permission denied | Missing Role/RoleBinding | Check ServiceAccount bindings and verb permissions |`,
945
- },
946
- {
947
- slug: "importing-yaml",
948
- title: "Importing Existing YAML",
949
- description: "Convert existing Kubernetes YAML manifests into typed TypeScript source files",
950
- content: `Chant can parse existing Kubernetes YAML manifests and generate typed TypeScript source files. This is useful for migrating existing infrastructure to chant.
951
-
952
- ## How it works
953
-
954
- \`\`\`
955
- Input YAML \u2192 parse \u2192 generate TypeScript \u2192 export typed resources
956
- \`\`\`
957
-
958
- The importer reads multi-document YAML, identifies each resource\u2019s \`apiVersion\` and \`kind\`, and generates the corresponding typed constructor call. The output is a valid \`.k8s.ts\` file you can immediately build with \`chant build\`.
959
-
960
- ## Running the import roundtrip
961
-
962
- From the \`lexicons/k8s\` directory:
963
-
964
- \`\`\`bash
965
- # Full roundtrip \u2014 clones kubernetes/examples, imports, serializes, compares
966
- just full-roundtrip
967
-
968
- # Skip clone if repo is already cached
969
- just full-roundtrip --skip-clone
970
-
971
- # Verbose output + filter to a specific manifest
972
- just full-roundtrip --skip-clone --verbose --manifest guestbook
973
-
974
- # Skip the serialize phase (parse + generate only)
975
- just full-roundtrip --skip-clone --skip-serialize
976
- \`\`\`
977
-
978
- ## Parse-only mode
979
-
980
- For quick validation without the full serialize cycle:
981
-
982
- \`\`\`bash
983
- just import-samples --skip-clone --verbose
984
-
985
- # Filter to a specific manifest
986
- just import-samples --skip-clone --verbose --manifest guestbook
987
- \`\`\`
988
-
989
- This runs the YAML \u2192 TypeScript generation but skips the round-trip comparison.
990
-
991
- ## What "pass" means
992
-
993
- A passing roundtrip means the serialized YAML, when re-parsed, produces the **same number of resources with matching \`kind\` values**. Exact YAML comparison is intentionally skipped \u2014 key ordering, quoting, and comments differ between input and output.
994
-
995
- ## Validating against a cluster
996
-
997
- To verify the serialized output is valid Kubernetes YAML, apply it to a local k3d cluster:
998
-
999
- \`\`\`bash
1000
- # Create a k3d cluster, apply serialized manifests, verify, tear down
1001
- just k3d-validate
1002
-
1003
- # Keep the cluster for manual inspection
1004
- just k3d-validate --keep-cluster
1005
-
1006
- # Reuse an existing cluster
1007
- just k3d-validate --reuse-cluster --verbose
1008
- \`\`\`
1009
-
1010
- See [Testing & Validation](/chant/lexicons/k8s/testing/) for full details on the k3d validation workflow.
1011
-
1012
- ## Limitations
1013
-
1014
- The import pipeline does not support:
1015
-
1016
- - **Helm charts** \u2014 template syntax (\`{{ .Values.x }}\`) is not valid YAML
1017
- - **Kustomize overlays** \u2014 overlays are processed by kustomize before producing YAML
1018
- - **Custom Resource Definitions (CRDs)** \u2014 only built-in K8s resource types are recognized
1019
- - **Cloud-specific volumes** \u2014 provider-specific volume types (awsElasticBlockStore, gcePersistentDisk) are parsed but excluded from k3d cluster validation (k3d doesn\u2019t provide cloud volume drivers)`,
1020
- },
1021
- {
1022
- slug: "testing",
1023
- title: "Testing & Validation",
1024
- description: "Roundtrip tests and k3d cluster validation for the Kubernetes lexicon",
1025
- content: `The Kubernetes lexicon includes scripts to verify the full import–serialize roundtrip and validate serialized YAML against a real cluster.
1026
-
1027
- ## Roundtrip tests
1028
-
1029
- The roundtrip test suite clones \`kubernetes/examples\` and runs each manifest through the full pipeline:
1030
-
1031
- \`\`\`
1032
- Input YAML → parse → generate TS → dynamic import → serialize YAML → re-parse → compare
1033
- \`\`\`
1034
-
1035
- **"Pass" means** the serialized YAML, when re-parsed, produces the same number of resources with matching \`kind\` values. Exact YAML comparison is intentionally skipped — key ordering, quoting, and comments differ between input and output.
1036
-
1037
- ### Running
1038
-
1039
- \`\`\`bash
1040
- cd lexicons/k8s
1041
-
1042
- # Full roundtrip (clones repo on first run)
1043
- just full-roundtrip
1044
-
1045
- # Skip clone if repo is already cached
1046
- just full-roundtrip --skip-clone
1047
-
1048
- # Parse + generate only (skip serialize phase)
1049
- just full-roundtrip --skip-clone --skip-serialize
1050
-
1051
- # Verbose output + filter to a specific manifest
1052
- just full-roundtrip --skip-clone --verbose --manifest guestbook
1053
- \`\`\`
1054
-
1055
- The test requires \`just generate\` to have been run first so the generated index exports real constructors. If the generated index is empty, the test falls back to parse-only mode automatically.
1056
-
1057
- ### Parse-only roundtrip
1058
-
1059
- The original parse-only roundtrip test is still available:
1060
-
1061
- \`\`\`bash
1062
- just import-samples --skip-clone --verbose
1063
- \`\`\`
1064
-
1065
- ## k3d cluster validation
1066
-
1067
- The k3d validation script applies serialized YAML to a real Kubernetes cluster to verify the output is valid and deployable.
1068
-
1069
- ### Prerequisites
1070
-
1071
- - [k3d](https://k3d.io/) — lightweight K8s cluster in Docker
1072
- - [kubectl](https://kubernetes.io/docs/tasks/tools/) — Kubernetes CLI
1073
- - Docker running
1074
-
1075
- ### Running
1076
-
1077
- \`\`\`bash
1078
- cd lexicons/k8s
1079
-
1080
- # Create cluster, run tests, delete cluster
1081
- just k3d-validate
1082
-
1083
- # Keep cluster for manual inspection
1084
- just k3d-validate --keep-cluster
1085
-
1086
- # Reuse an existing cluster
1087
- just k3d-validate --reuse-cluster --verbose
1088
- \`\`\`
1089
-
1090
- After \`--keep-cluster\`, inspect resources manually:
1091
-
1092
- \`\`\`bash
1093
- kubectl get all
1094
- kubectl get deployments,services
1095
- \`\`\`
1096
-
1097
- ### Safe manifest allowlist
1098
-
1099
- The script uses a curated allowlist of ~14 manifests known to work on bare k3d (no cloud volumes, CRDs, or GPUs). These include:
1100
-
1101
- - **Guestbook** — Deployments and Services for frontend + Redis
1102
- - **Cassandra** — Headless Service
1103
- - **vLLM** — Service
1104
-
1105
- ### What it validates
1106
-
1107
- For each manifest the script:
1108
-
1109
- 1. Runs the full roundtrip (YAML → Chant DSL → YAML)
1110
- 2. Applies the serialized YAML with \`kubectl apply\`
1111
- 3. Verifies the resource exists with \`kubectl get\`
1112
-
1113
- A failure at any stage is reported with the specific phase that failed.
1114
-
1115
- ## See also
1116
-
1117
- - [Importing Existing YAML](/chant/lexicons/k8s/importing-yaml/) — convert existing K8s manifests into typed TypeScript`,
1118
- },
1119
- {
1120
- slug: "skills",
1121
- title: "AI Skills",
1122
- description: "AI agent skills bundled with the Kubernetes lexicon",
1123
- content: `The Kubernetes lexicon ships an AI skill called **chant-k8s** that teaches AI coding agents how to build, validate, and deploy Kubernetes manifests from a chant project.
1124
-
1125
- ## What are skills?
1126
-
1127
- Skills are structured markdown documents bundled with a lexicon. When an AI agent works in a chant project, it discovers and loads relevant skills automatically — giving it operational knowledge about the deployment workflow without requiring the user to explain each step.
1128
-
1129
- ## Installation
1130
-
1131
- When you scaffold a new project with \`chant init --lexicon k8s\`, the skill is installed to \`skills/chant-k8s/SKILL.md\` for automatic discovery by Claude Code.
1132
-
1133
- ## Skill: chant-k8s
1134
-
1135
- The \`chant-k8s\` skill covers the full deployment lifecycle:
1136
-
1137
- - **Build** — \`chant build src/ --output manifests.yaml\`
1138
- - **Lint** — \`chant lint src/\` + post-synth checks (20 rules)
1139
- - **Apply** — \`kubectl apply -f manifests.yaml\`
1140
- - **Status** — \`kubectl get pods,svc,deploy\`
1141
- - **Rollback** — \`kubectl rollout undo deployment/my-app\`
1142
- - **Troubleshooting** — pod status, logs, events, common error patterns
1143
-
1144
- The skill is invocable as a slash command: \`/chant-k8s\`
1145
-
1146
- The full playbook is also available as a [documentation page](/chant/lexicons/k8s/operational-playbook/).
1147
-
1148
- ## MCP integration
1149
-
1150
- The lexicon also provides MCP (Model Context Protocol) tools and resources:
1151
-
1152
- | MCP tool | Description |
1153
- |----------|-------------|
1154
- | \`diff\` | Compare current build output against previous |
1155
-
1156
- | MCP resource | Description |
1157
- |--------------|-------------|
1158
- | \`resource-catalog\` | JSON list of all supported K8s resource types |
1159
- | \`examples/basic-deployment\` | Example Deployment + Service code |`,
1160
- },
1161
- ],
1162
138
  basePath: "/chant/lexicons/k8s/",
1163
- sidebarExtra: [
1164
- {
1165
- label: "Live Cluster",
1166
- items: [
1167
- { label: "The API Client", slug: "api-client" },
1168
- { label: "chant kube", slug: "kube" },
1169
- ],
1170
- },
1171
- { label: "CRD-Generated Classes", slug: "crd-classes" },
1172
- { label: "Argo CD Composites", slug: "argo-composites" },
1173
- { label: "Flux Composites", slug: "flux-composites" },
1174
- {
1175
- label: "Vendor Composites",
1176
- items: [
1177
- { label: "EKS Composites", slug: "eks-composites" },
1178
- { label: "AKS Composites", slug: "aks-composites" },
1179
- { label: "GKE Composites", slug: "gke-composites" },
1180
- ],
1181
- },
1182
- ],
1183
139
  };
1184
140
 
1185
141
  const result = await docsPipeline(config);