timonel 3.1.1 → 3.1.2

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 (83) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/README.md +323 -695
  3. package/dist/cli.js +275 -15
  4. package/dist/index.d.ts +5 -1
  5. package/dist/index.js +12 -0
  6. package/dist/lib/helm.d.ts +471 -0
  7. package/dist/lib/helm.js +483 -0
  8. package/dist/lib/helmChartWriter.d.ts +157 -0
  9. package/dist/lib/helmChartWriter.js +171 -1
  10. package/dist/lib/policy/configurationLoader.d.ts +132 -0
  11. package/dist/lib/policy/configurationLoader.js +132 -0
  12. package/dist/lib/policy/errorContextGenerator.d.ts +89 -0
  13. package/dist/lib/policy/errorContextGenerator.js +99 -2
  14. package/dist/lib/policy/errors.d.ts +35 -0
  15. package/dist/lib/policy/errors.js +36 -0
  16. package/dist/lib/policy/index.d.ts +9 -0
  17. package/dist/lib/policy/index.js +16 -0
  18. package/dist/lib/policy/parallelExecutor.d.ts +90 -0
  19. package/dist/lib/policy/parallelExecutor.js +86 -3
  20. package/dist/lib/policy/pluginLoader.d.ts +92 -0
  21. package/dist/lib/policy/pluginLoader.js +92 -1
  22. package/dist/lib/policy/pluginRegistry.d.ts +54 -0
  23. package/dist/lib/policy/pluginRegistry.js +56 -0
  24. package/dist/lib/policy/policyEngine.d.ts +137 -0
  25. package/dist/lib/policy/policyEngine.js +191 -5
  26. package/dist/lib/policy/resultAggregator.d.ts +46 -0
  27. package/dist/lib/policy/resultAggregator.js +69 -1
  28. package/dist/lib/policy/resultFormatter.d.ts +88 -0
  29. package/dist/lib/policy/resultFormatter.js +101 -0
  30. package/dist/lib/policy/types.d.ts +136 -0
  31. package/dist/lib/policy/types.js +8 -0
  32. package/dist/lib/policy/validationCache.d.ts +146 -0
  33. package/dist/lib/policy/validationCache.js +142 -6
  34. package/dist/lib/resources/baseResourceProvider.d.ts +45 -0
  35. package/dist/lib/resources/baseResourceProvider.js +48 -1
  36. package/dist/lib/resources/cloud/aws/awsResources.d.ts +192 -0
  37. package/dist/lib/resources/cloud/aws/awsResources.js +163 -1
  38. package/dist/lib/resources/cloud/aws/karpenterResources.d.ts +131 -0
  39. package/dist/lib/resources/cloud/aws/karpenterResources.js +77 -0
  40. package/dist/lib/rutter.d.ts +381 -3
  41. package/dist/lib/rutter.js +439 -28
  42. package/dist/lib/security.d.ts +123 -0
  43. package/dist/lib/security.js +162 -4
  44. package/dist/lib/templates/flexible-subchart.d.ts +52 -0
  45. package/dist/lib/templates/flexible-subchart.js +70 -0
  46. package/dist/lib/templates/umbrella-chart.d.ts +27 -0
  47. package/dist/lib/templates/umbrella-chart.js +89 -0
  48. package/dist/lib/types.d.ts +26 -0
  49. package/dist/lib/umbrella.d.ts +23 -0
  50. package/dist/lib/umbrella.js +23 -0
  51. package/dist/lib/umbrellaRutter.d.ts +75 -0
  52. package/dist/lib/umbrellaRutter.js +82 -2
  53. package/dist/lib/utils/envVarsLoader.d.ts +49 -0
  54. package/dist/lib/utils/envVarsLoader.js +53 -0
  55. package/dist/lib/utils/helmConstructSerializer.d.ts +17 -0
  56. package/dist/lib/utils/helmConstructSerializer.js +22 -0
  57. package/dist/lib/utils/helmControlStructures.d.ts +194 -0
  58. package/dist/lib/utils/helmControlStructures.js +180 -0
  59. package/dist/lib/utils/helmHelpers/envHelpers.d.ts +13 -0
  60. package/dist/lib/utils/helmHelpers/envHelpers.js +13 -0
  61. package/dist/lib/utils/helmHelpers/gitopsHelpers.d.ts +13 -0
  62. package/dist/lib/utils/helmHelpers/gitopsHelpers.js +13 -0
  63. package/dist/lib/utils/helmHelpers/index.d.ts +74 -0
  64. package/dist/lib/utils/helmHelpers/index.js +85 -1
  65. package/dist/lib/utils/helmHelpers/observabilityHelpers.d.ts +13 -0
  66. package/dist/lib/utils/helmHelpers/observabilityHelpers.js +13 -0
  67. package/dist/lib/utils/helmHelpers/types.d.ts +23 -0
  68. package/dist/lib/utils/helmHelpers/types.js +4 -0
  69. package/dist/lib/utils/helmHelpers/validationHelpers.d.ts +13 -0
  70. package/dist/lib/utils/helmHelpers/validationHelpers.js +13 -0
  71. package/dist/lib/utils/helmHelpers.d.ts +62 -0
  72. package/dist/lib/utils/helmHelpers.js +77 -0
  73. package/dist/lib/utils/helmYamlSerializer.d.ts +77 -0
  74. package/dist/lib/utils/helmYamlSerializer.js +398 -21
  75. package/dist/lib/utils/logger.d.ts +153 -0
  76. package/dist/lib/utils/logger.js +170 -2
  77. package/dist/lib/utils/valuesRef.d.ts +181 -50
  78. package/dist/lib/utils/valuesRef.js +168 -170
  79. package/dist/lib/validation/inputValidator.d.ts +45 -0
  80. package/dist/lib/validation/inputValidator.js +67 -2
  81. package/dist/types/index.d.ts +34 -0
  82. package/dist/types/index.js +3 -0
  83. package/package.json +31 -38
package/README.md CHANGED
@@ -10,832 +10,460 @@
10
10
  [![TypeScript][ts-badge]][ts-url]
11
11
  [![Maintained by KenkoGeek][maintained-badge]][maintained-url]
12
12
 
13
- **Timonel** (Spanish for "helmsman") is a powerful TypeScript library that programmatically generates
14
- Helm charts using cdk8s. Define Kubernetes resources with type-safe classes and synthesize complete
15
- Helm charts with `Chart.yaml`, `values.yaml`, environment-specific values files, and `templates/`
16
- directory.
17
-
18
- ## ✨ Key Features
19
-
20
- ### Core Capabilities
21
-
22
- - **🔒 Type-safe API** with strict TypeScript and cdk8s constructs
23
- - **🔧 Flexible resource creation** with built-in methods and `addManifest()` for custom resources
24
- - **🌍 Multi-environment support** with automatic values files generation
25
- - **☂️ Umbrella Charts** for managing multiple subcharts as a single unit
26
- - **⚡ Minimal CLI** (`tl`) for scaffolding, synthesis, validation, and deployment
27
- - **📦 Flexible subchart templates** supporting both cdk8s and cdk8s-plus-33
28
-
29
- ### Type-Safe Helm Helpers (v3.0+)
30
-
31
- #### ValuesRef System (NEW in v3.0 - RECOMMENDED)
32
-
33
- Type-safe proxy-based values references with full IDE support:
34
-
35
- - Import: `import { valuesRef } from 'timonel'`
36
- - **Comparison operators**: `eq`, `ne`, `gt`, `ge`, `lt`, `le`
37
- - **Logical operators**: `not`, `and`, `or`
38
- - **String functions**: `quote`, `upper`, `lower`, `trim`, `replace`, `contains`
39
- - **Default values**: `default()`
40
- - **Type checking**: `kindIs`, `hasKey`
41
- - **YAML functions**: `toYaml`, `toJson`, `nindent`, `indent`
42
- - **Field-level conditionals**: `v.if()`, `v.ifElse()` - Complex conditional logic
43
- - **Range loops**: `v.range()` - Type-safe iteration
44
- - **Context switching**: `v.with()` - Scoped value access
45
-
46
- #### Composable Helpers
47
-
48
- Template definition and inclusion helpers:
49
-
50
- - `helmInclude`, `helmDefine`, `helmVar`, `helmBlock`, `helmComment`, `helmFragment`
51
- - `template`, `include`, `quote`, `indent`
52
-
53
- #### Value Reference Helpers
54
-
55
- Useful string-based utilities (no ValuesRef equivalent):
56
-
57
- - `requiredValuesRef` - Required value with validation
58
- - `numberRef`, `boolRef`, `floatRef` - Type-cast references (int, bool, float64)
59
- - `base64Ref` - Base64 encoding
60
-
61
- #### Legacy Helpers (NOT RECOMMENDED)
62
-
63
- **⚠️ Use ValuesRef system instead:**
64
-
65
- - `valuesRef(path)` → use `v.path` (ValuesRef system)
66
- - `stringRef()` → use `v.quote()` (ValuesRef system)
67
- - `defaultRef()` → use `v.default()` (ValuesRef system)
68
- - `jsonRef()` → use `v.toJson()` (ValuesRef system)
69
- - `conditionalRef()` → use `v.if()` (ValuesRef system)
70
- - `helmIf`, `helmIfSimple` → use `v.if()` (ValuesRef system)
71
- - `helmRange` → use `v.range()` (ValuesRef system)
72
- - `helmWith` → use `v.with()` (ValuesRef system)
73
- - `helmIfElseIf` → use `v.if()` with nested conditions
74
-
75
- ### Enhanced Helm Helpers
76
-
77
- - **Environment Helpers**: `envRef`, `envDefault`, `envRequired`, `envFromSecret`, `envFromConfigMap`
78
- - **GitOps Helpers**: `gitBranch`, `gitCommit`, `gitTag`, `gitopsAnnotations`
79
- - **Observability Helpers**: `prometheusAnnotations`, `datadogAnnotations`, `tracingAnnotations`
80
- - **Validation Helpers**: `validateRequired`, `validatePattern`, `validateRange`, `validateEnum`
81
- - **Standard Helpers**: 40+ built-in Helm helpers (chart.name, chart.fullname, chart.labels, etc.)
82
-
83
- ### Cloud Integrations
84
-
85
- - **AWS Resources**:
86
- - EBS/EFS StorageClass with encryption and performance options
87
- - ALB Ingress with SSL/TLS and health checks
88
- - IRSA ServiceAccount for pod-level IAM roles
89
- - ECR integration
90
- - Karpenter NodePool, NodeClaim, and EC2NodeClass
91
- - **Karpenter Features**:
92
- - Disruption budgets and consolidation policies
93
- - Instance type selection and requirements
94
- - Spot instance support
95
- - Custom AMI and user data
96
-
97
- ### Security & Validation
98
-
99
- - **🛡️ Security-first approach**:
100
- - Input validation (CWE-20, CWE-22/23)
101
- - Path traversal prevention
102
- - Command injection prevention (CWE-78/77/88)
103
- - Log injection protection (CWE-117)
104
- - Code injection prevention (CWE-94)
105
- - **🔍 Policy Engine** (NEW):
106
- - Extensible validation framework for Kubernetes manifests
107
- - Plugin-based architecture for custom policy rules
108
- - Zero-impact integration (completely optional)
109
- - Support for security, compliance, and best practice policies
110
- - **NetworkPolicy support** for pod-level network isolation
111
- - **Helm chart validation** with `validateHelmYaml`
112
- - **SecurityUtils** for path validation and sanitization
113
-
114
- ### Developer Experience
13
+ **Timonel** is a TypeScript library for generating complete Helm charts programmatically on top of
14
+ cdk8s and cdk8s-plus. Kubernetes resources stay typed in application code while Timonel handles
15
+ Helm chart structure, values files, helpers, environment variants, policy validation, and final
16
+ chart output.
115
17
 
116
- - **Structured logging** with Pino (JSON format, performance tracking)
117
- - **Environment variables loader** for external configuration
118
- - **YAML serialization** with Helm template preservation
119
- - **TypeScript strict mode** with all compiler checks enabled
120
- - **Comprehensive error handling** with detailed messages
121
-
122
- ## 🚀 Quick Start
123
-
124
- ### Installation
18
+ The library API is the primary product. The `tl` CLI is a convenience layer for scaffolding,
19
+ synthesis, Helm validation, deployment, and umbrella-chart workflows.
125
20
 
126
- ```bash
127
- # Install Timonel globally
128
- npm install -g timonel
21
+ ## Architecture
129
22
 
130
- # Or use with pnpm
131
- pnpm add -g timonel
132
- ```
23
+ Timonel is intentionally **typed-first**. Use Kubernetes resource APIs in this order:
133
24
 
134
- ### Basic Usage
25
+ 1. `cdk8s-plus-33` when it already provides the resource abstraction;
26
+ 2. cdk8s `ApiObject` or a focused typed Timonel abstraction when a higher-level construct does not
27
+ fit;
28
+ 3. object-form `Rutter.addManifest()` for CRDs or custom resources without a suitable typed
29
+ construct;
30
+ 4. raw YAML only as a legacy escape hatch.
135
31
 
136
- ```bash
137
- # Create your first chart
138
- tl init my-app
32
+ The raw-string `addManifest()` overload and `addTemplateManifest()` are deprecated and planned for
33
+ removal in the next major release.
139
34
 
140
- # Generate Helm chart
141
- tl synth my-app my-app-dist
35
+ ## Requirements
142
36
 
143
- # Use with Helm
144
- helm install my-app my-app-dist
145
- ```
37
+ - Node.js `^22.22.2`, `^24.15.0`, or `>=26.0.0`
38
+ - pnpm `>=9`
39
+ - Helm 3 when using `tl validate`, `tl deploy`, or manual chart validation
146
40
 
147
- ### Umbrella Charts
41
+ ## Installation
148
42
 
149
43
  ```bash
150
- # Create umbrella chart structure
151
- tl umbrella init my-umbrella-app
152
-
153
- # Add subcharts
154
- tl umbrella add frontend
155
- tl umbrella add backend
156
-
157
- # Generate umbrella chart
158
- tl umbrella synth
44
+ pnpm add timonel cdk8s cdk8s-plus-33 constructs
159
45
  ```
160
46
 
161
- ## 💡 Examples
47
+ npm and other compatible package managers can also install the package.
162
48
 
163
- ### Simple Web Application
49
+ ## Quick start
164
50
 
165
51
  ```typescript
166
- import { Rutter, helmInclude, createHelmExpression as helm } from 'timonel';
52
+ import * as kplus from 'cdk8s-plus-33';
53
+ import { Rutter } from 'timonel';
167
54
 
168
55
  const chart = new Rutter({
169
56
  meta: {
170
- name: 'web-app',
57
+ name: 'my-app',
171
58
  version: '1.0.0',
172
- description: 'Simple web application',
59
+ description: 'Typed Helm chart generated by Timonel',
173
60
  },
174
61
  defaultValues: {
175
- replicas: 3,
176
- image: {
177
- repository: 'nginx',
178
- tag: 'latest',
179
- },
62
+ environment: 'production',
180
63
  },
181
64
  });
182
65
 
183
- // Add Deployment with type-safe helpers
184
- chart.addManifest(
185
- {
186
- apiVersion: 'apps/v1',
187
- kind: 'Deployment',
188
- metadata: {
189
- name: helmInclude('chart.fullname', '.'),
190
- labels: helmInclude('chart.labels', '.', { pipe: 'nindent 4' }),
191
- },
192
- spec: {
193
- replicas: helm('{{ .Values.replicas }}'),
194
- selector: {
195
- matchLabels: helmInclude('chart.selectorLabels', '.', { pipe: 'nindent 6' }),
196
- },
197
- template: {
198
- metadata: {
199
- labels: helmInclude('chart.selectorLabels', '.', { pipe: 'nindent 8' }),
200
- },
201
- spec: {
202
- containers: [
203
- {
204
- name: 'web',
205
- image: helm('{{ .Values.image.repository }}:{{ .Values.image.tag }}'),
206
- ports: [{ containerPort: 80, name: 'http' }],
207
- },
208
- ],
209
- },
66
+ const deployment = new kplus.Deployment(chart.getChart(), 'App', {
67
+ metadata: { name: 'my-app' },
68
+ containers: [
69
+ {
70
+ name: 'app',
71
+ image: 'nginx:1.27',
72
+ portNumber: 80,
73
+ resources: {
74
+ cpu: { request: kplus.Cpu.millis(100) },
210
75
  },
211
76
  },
212
- },
213
- 'deployment',
214
- );
215
-
216
- // Generate the chart
217
- chart.write('./dist');
218
- ```
219
-
220
- ### Policy Engine Integration
221
-
222
- ```typescript
223
- import { Rutter, PolicyEngine } from 'timonel';
224
- import { securityPolicies } from '@mycompany/k8s-security-policies';
225
-
226
- // Create policy engine with custom plugins
227
- const policyEngine = new PolicyEngine().use(securityPolicies).configure({
228
- timeout: 5000,
229
- parallel: true,
77
+ ],
230
78
  });
231
79
 
232
- const chart = new Rutter({
233
- meta: {
234
- name: 'secure-app',
235
- version: '1.0.0',
236
- },
237
- // Optional policy validation
238
- policyEngine,
80
+ deployment.exposeViaService();
81
+
82
+ new kplus.HorizontalPodAutoscaler(chart.getChart(), 'AppHpa', {
83
+ target: deployment,
84
+ minReplicas: 1,
85
+ maxReplicas: 5,
239
86
  });
240
87
 
241
- // Policies validate manifests before chart generation
242
- chart.write('./dist'); // Fails if policy violations found
88
+ await chart.write('./dist/my-app');
243
89
  ```
244
90
 
245
- ### Creating Custom Policy Plugins
91
+ The generated chart contains `Chart.yaml`, `values.yaml`, `templates/`, `_helpers.tpl`, and
92
+ `.helmignore`.
246
93
 
247
- ```typescript
248
- import { PolicyPlugin, PolicyViolation } from 'timonel';
94
+ Validate the final Helm output:
249
95
 
250
- export const mySecurityPolicy: PolicyPlugin = {
251
- name: 'my-security-policy',
252
- version: '1.0.0',
253
- description: 'Custom security validation rules',
254
-
255
- async validate(manifests, context) {
256
- const violations: PolicyViolation[] = [];
257
-
258
- for (const manifest of manifests) {
259
- if (manifest.kind === 'Deployment') {
260
- // Check for security context
261
- if (!manifest.spec?.template?.spec?.securityContext) {
262
- violations.push({
263
- plugin: this.name,
264
- severity: 'error',
265
- message: 'Deployment must specify securityContext',
266
- resourcePath: `${manifest.kind}/${manifest.metadata?.name}`,
267
- suggestion: 'Add spec.template.spec.securityContext to your Deployment',
268
- });
269
- }
270
- }
271
- }
272
-
273
- return violations;
274
- },
275
- };
96
+ ```bash
97
+ helm lint ./dist/my-app
98
+ helm template my-app ./dist/my-app
276
99
  ```
277
100
 
278
- ### Umbrella Chart with Multiple Services
101
+ ## `Rutter`
279
102
 
280
- ```typescript
281
- import { UmbrellaChartTemplate } from 'timonel';
103
+ `Rutter` owns a cdk8s `Chart` and converts its resource tree into Helm chart assets.
282
104
 
283
- const umbrellaConfig = {
284
- name: 'my-umbrella-app',
285
- version: '1.0.0',
286
- subcharts: [
287
- {
288
- name: 'frontend',
289
- version: '1.0.0',
290
- chart: frontendChartFunction,
291
- },
292
- {
293
- name: 'backend',
294
- version: '1.0.0',
295
- chart: backendChartFunction,
105
+ ```typescript
106
+ const chart = new Rutter({
107
+ meta: { name: 'orders', version: '1.0.0' },
108
+ namespace: 'orders',
109
+ defaultValues: {
110
+ replicas: 2,
111
+ },
112
+ envValues: {
113
+ production: {
114
+ replicas: 4,
296
115
  },
297
- ],
298
- };
299
-
300
- export const umbrella = new UmbrellaChartTemplate(umbrellaConfig);
116
+ },
117
+ });
301
118
  ```
302
119
 
303
- ### Using Type-Safe Helm Helpers
120
+ Useful methods include:
121
+
122
+ | API | Purpose |
123
+ | ------------------------- | --------------------------------------------------------- |
124
+ | `getChart()` | Access the real cdk8s `Chart` for native typed constructs |
125
+ | `write(outDir)` | Synthesize and write the complete Helm chart |
126
+ | `toSynthArray()` | Asynchronously synthesize Helm assets |
127
+ | `getMeta()` | Read chart metadata |
128
+ | `getDefaultValues()` | Read default values |
129
+ | `getEnvValues()` | Read environment-specific values |
130
+ | `addManifest(object, id)` | Object fallback for custom resources |
304
131
 
305
- #### ValuesRef System (Recommended)
132
+ `toSynthArraySync()` remains for compatibility and is deprecated. It cannot be used with a policy
133
+ engine.
306
134
 
307
- The new ValuesRef system provides a type-safe, proxy-based approach to Helm values with full IDE support.
135
+ ### Existing construct tree
308
136
 
309
- **⚠️ Important:** This is completely different from the legacy `valuesRef(path: string)` helper.
310
- The new system uses generics and returns a proxy object with methods.
137
+ Pass `scope` when Timonel should participate in an existing cdk8s/constructs tree:
311
138
 
312
139
  ```typescript
313
- import { valuesRef } from 'timonel';
140
+ import { App } from 'cdk8s';
141
+ import * as kplus from 'cdk8s-plus-33';
142
+ import { Rutter } from 'timonel';
314
143
 
315
- interface MyValues {
316
- replicaCount: number;
317
- image: { repository: string; tag: string };
318
- autoscaling: { enabled: boolean; minReplicas: number };
319
- }
144
+ const app = new App();
145
+ const chart = new Rutter({
146
+ scope: app,
147
+ meta: { name: 'shared-tree', version: '1.0.0' },
148
+ });
320
149
 
321
- const v = valuesRef<MyValues>();
150
+ new kplus.ConfigMap(chart.getChart(), 'Config', {
151
+ metadata: { name: 'shared-tree' },
152
+ data: { mode: 'production' },
153
+ });
154
+ ```
322
155
 
323
- // Type-safe value references with IDE autocomplete
324
- const replicas = v.replicaCount; // {{ .Values.replicaCount }}
325
- const imageTag = v.image.tag; // {{ .Values.image.tag }}
156
+ ## Type-safe Helm values
326
157
 
327
- // Comparison operators
328
- const isProd = v.environment.eq('production'); // eq .Values.environment "production"
329
- const hasReplicas = v.replicaCount.gt(1); // gt .Values.replicaCount 1
158
+ `valuesRef<T>()` exposes Helm values using the shape of a TypeScript type.
330
159
 
331
- // Logical operators
332
- const notEnabled = v.autoscaling.enabled.not(); // not .Values.autoscaling.enabled
160
+ ```typescript
161
+ import { valuesRef } from 'timonel';
333
162
 
334
- // String functions
335
- const upperEnv = v.environment.upper(); // .Values.environment | upper
336
- const quotedTag = v.image.tag.quote(); // .Values.image.tag | quote
163
+ interface Values {
164
+ environment: string;
165
+ replicas: number;
166
+ image: {
167
+ repository: string;
168
+ tag: string;
169
+ };
170
+ autoscaling: {
171
+ enabled: boolean;
172
+ };
173
+ env: Array<{
174
+ name: string;
175
+ value: string;
176
+ }>;
177
+ }
337
178
 
338
- // Default values
339
- const port = v.port.default(8080); // {{ .Values.port | default 8080 }}
179
+ const v = valuesRef<Values>();
340
180
 
341
- // Field-level conditionals
342
- const deployment = {
343
- spec: {
344
- replicas: v.if(notEnabled, v.replicaCount), // Conditionally include field
345
- },
346
- };
181
+ const imageTag = v.image.tag.quote();
182
+ const multipleReplicas = v.replicas.gt(1);
183
+ const production = v.environment.eq('production');
184
+ const fixedTag = v.image.tag.default('1.0.0');
347
185
 
348
- // Range loops
349
- const envVars = v.env.range((item, index) => ({
186
+ const env = v.env.range((item) => ({
350
187
  name: item.name,
351
188
  value: item.value,
352
189
  }));
353
190
 
354
- // Context switching
355
- const dbConfig = v.database.with((db) => ({
356
- host: db.host,
357
- port: db.port,
358
- }));
191
+ const replicasField = v.replicas.if(v.autoscaling.enabled.not(), v.replicas);
359
192
  ```
360
193
 
361
- #### Template Composition Helpers
194
+ The compiler rejects value paths that do not exist in `Values`.
362
195
 
363
- Use these helpers for template definitions and inclusions:
196
+ ### Reserved values keys
364
197
 
365
- ```typescript
366
- import { helmInclude, helmDefine, helmFragment } from 'timonel';
367
-
368
- // Template inclusion with pipe
369
- const labels = helmInclude('chart.labels', '.', { pipe: 'nindent 4' });
370
-
371
- // Define a named template
372
- const myTemplate = helmDefine('myapp.config', {
373
- key: 'value',
374
- });
375
-
376
- // Combine multiple constructs
377
- const combined = helmFragment(helmInclude('chart.labels', '.'), { customKey: 'customValue' });
378
- ```
379
-
380
- #### Legacy Flow Control (NOT RECOMMENDED)
381
-
382
- **⚠️ These are legacy and NOT RECOMMENDED. Use ValuesRef system (v.if, v.range, v.with)
383
- instead:**
198
+ Names used by the proxy API, such as `default`, `range`, and `with`, and root helpers such as
199
+ `release`, `chart`, and `capabilities`, are accessed with the typed `at()` method when those names
200
+ also exist in your values schema:
384
201
 
385
202
  ```typescript
386
- // OLD WAY - string-based valuesRef (no type safety)
387
- const oldRef = valuesRef('.Values.production');
388
-
389
- // ✅ NEW WAY - ValuesRef system (type-safe)
390
- const v = valuesRef<MyValues>();
391
- const newRef = v.production; // {{ .Values.production }}
203
+ interface Values {
204
+ release: { name: string };
205
+ settings: { default: string };
206
+ }
392
207
 
393
- // OLD WAY - helmIf, helmRange, helmWith (string-based)
394
- const config = helmIf('.Values.production', { replicas: 5 }, { replicas: 1 });
208
+ const v = valuesRef<Values>();
395
209
 
396
- // NEW WAY - v.if(), v.range(), v.with() (type-safe)
397
- const config = v.if(v.production, { replicas: 5 });
210
+ const releaseName = v.at('release').name;
211
+ const defaultSetting = v.settings.at('default');
398
212
  ```
399
213
 
400
- **Why ValuesRef is Better:**
401
-
402
- - ✅ **100% Type-Safe** - Catch errors at compile time with TypeScript generics
403
- - ✅ **No Raw Strings** - Eliminate manual template interpolation and typos
404
- - ✅ **Full IDE Support** - Autocomplete, type hints, and refactoring support
405
- - ✅ **Proxy-based** - Chainable methods for complex logic
406
- - ✅ **Composable** - Nest and combine operations naturally
407
- - ❌ **Legacy helpers** - String-based, error-prone, no IDE support
408
-
409
- **Migration:** Replace `valuesRef(path)` with `v.path`. Replace `helmIf`, `helmRange`, `helmWith`
410
- with `v.if()`, `v.range()`, `v.with()`.
214
+ `at()` accepts only keys from the current TypeScript values type.
411
215
 
412
- **Learn more:** See the
413
- [Type-Safe Helm Helpers Guide](https://github.com/KenkoGeek/timonel/wiki/Helm-Helpers-System) for
414
- complete documentation, examples, and best practices.
216
+ ## Custom resources
415
217
 
416
- ## 🔍 Policy Engine
218
+ Use object-form `addManifest()` when no appropriate typed construct is available:
417
219
 
418
- The Policy Engine provides extensible validation for Kubernetes manifests through a
419
- plugin-based architecture. It's completely optional and has zero impact on existing users.
220
+ ```typescript
221
+ chart.addManifest(
222
+ {
223
+ apiVersion: 'monitoring.coreos.com/v1',
224
+ kind: 'ServiceMonitor',
225
+ metadata: { name: 'orders' },
226
+ spec: {
227
+ selector: {
228
+ matchLabels: { app: 'orders' },
229
+ },
230
+ endpoints: [{ port: 'http' }],
231
+ },
232
+ },
233
+ 'OrdersServiceMonitor',
234
+ );
235
+ ```
420
236
 
421
- ### Key Features
237
+ Do not switch a standard Kubernetes resource to raw YAML merely because one field contains Helm
238
+ logic. Prefer typed constructs and Timonel's Helm-value helpers where they fit.
422
239
 
423
- - **🔌 Plugin Architecture**: Extensible through external npm packages
424
- - **⚡ Zero Impact**: Completely optional with no performance overhead when unused
425
- - **🛡️ Security Focus**: Built-in support for security and compliance policies
426
- - **🔄 Async Support**: Handles both synchronous and asynchronous validation plugins
427
- - **📊 Rich Reporting**: Detailed violation reports with suggestions and context
428
- - **⏱️ Timeout Protection**: Configurable timeouts prevent hanging validations
429
- - **🔧 Configurable**: Environment-specific policy configuration support
430
- - **🚀 Performance Optimized**: Parallel execution, caching, and resource monitoring
431
- - **🔄 Error Resilience**: Graceful degradation and retry mechanisms
432
- - **📈 Observability**: Structured logging and performance metrics
240
+ ## Umbrella charts
433
241
 
434
- ### Quick Start
242
+ `UmbrellaRutter` combines multiple `Rutter` instances as Helm dependencies:
435
243
 
436
244
  ```typescript
437
- import { Rutter, PolicyEngine } from 'timonel';
245
+ import * as kplus from 'cdk8s-plus-33';
246
+ import { Rutter, UmbrellaRutter } from 'timonel';
438
247
 
439
- // Optional: Add policy validation
440
- const policyEngine = new PolicyEngine({
441
- timeout: 10000,
442
- parallel: true,
443
- gracefulDegradation: true,
444
- });
248
+ function serviceChart(name: string): Rutter {
249
+ const chart = new Rutter({
250
+ meta: { name, version: '1.0.0' },
251
+ });
252
+
253
+ new kplus.ConfigMap(chart.getChart(), 'Config', {
254
+ metadata: { name: `${name}-config` },
255
+ });
445
256
 
446
- // Register plugins
447
- await policyEngine.use(await import('@mycompany/security-policies'));
448
- await policyEngine.use(await import('@kubernetes/best-practices'));
257
+ return chart;
258
+ }
449
259
 
450
- const chart = new Rutter({
451
- meta: { name: 'my-app', version: '1.0.0' },
452
- policyEngine, // ← Completely optional
260
+ const umbrella = new UmbrellaRutter({
261
+ meta: {
262
+ name: 'commerce',
263
+ version: '1.0.0',
264
+ },
265
+ subcharts: [
266
+ { name: 'catalog', version: '1.0.0', rutter: serviceChart('catalog') },
267
+ { name: 'orders', version: '1.0.0', rutter: serviceChart('orders') },
268
+ ],
453
269
  });
454
270
 
455
- chart.write('./dist'); // Validates before writing
271
+ await umbrella.write('./dist/commerce');
456
272
  ```
457
273
 
458
- ### Available Policy Plugins
274
+ The CLI also supports dependency and inline umbrella synthesis modes.
459
275
 
460
- **Built-in Examples:**
276
+ ## AWS and EKS helpers
461
277
 
462
- - **Security Plugin** - Comprehensive security validation (security contexts, RBAC, network policies)
463
- - **Best Practices Plugin** - Kubernetes best practices (resource limits, naming, probes)
464
- - **AWS Plugin** - AWS-specific validations (EKS, ALB, IRSA, cost optimization)
278
+ Timonel includes focused helpers for Kubernetes-side AWS integrations:
465
279
 
466
- **Community Plugins:**
280
+ - EBS and EFS `StorageClass` resources;
281
+ - IRSA and ECR-oriented `ServiceAccount` resources;
282
+ - ALB `Ingress` configuration;
283
+ - Karpenter `NodePool`, `NodeClaim`, and `EC2NodeClass` resources;
284
+ - convenience NodePool builders for disruption and scheduling policies.
467
285
 
468
- - `@kubernetes/pod-security-standards` - Official Kubernetes PSS validation
469
- - `@open-policy-agent/timonel-plugin` - OPA Rego policy integration
470
- - `@falco/security-policies` - Falco runtime security rules
286
+ Example:
287
+
288
+ ```typescript
289
+ chart.addAWSIRSAServiceAccount({
290
+ name: 'orders',
291
+ roleArn: 'arn:aws:iam::123456789012:role/orders',
292
+ });
293
+ ```
471
294
 
472
- **Enterprise Plugins:**
295
+ These APIs generate Kubernetes resources. They do **not** create IAM roles, storage systems, ECR
296
+ repositories, controllers, cluster networking, or Karpenter CRDs in AWS.
473
297
 
474
- - `@company/compliance-policies` - Organization-specific compliance rules
475
- - `@aws/well-architected-policies` - AWS Well-Architected Framework validation
476
- - `@security/cis-benchmarks` - CIS Kubernetes Benchmark validation
298
+ ## Policy Engine
477
299
 
478
- ### Creating Custom Policies
300
+ The optional Policy Engine validates synthesized Kubernetes manifests through user-supplied
301
+ plugins before Timonel writes the Helm chart.
479
302
 
480
303
  ```typescript
481
- import { PolicyPlugin, PolicyViolation, ValidationContext } from 'timonel';
304
+ import type { PolicyPlugin } from 'timonel';
305
+ import { PolicyEngine, Rutter } from 'timonel';
482
306
 
483
- export const customSecurityPolicy: PolicyPlugin = {
484
- name: 'custom-security-policy',
307
+ const requireMetadataName: PolicyPlugin = {
308
+ name: 'require-metadata-name',
485
309
  version: '1.0.0',
486
- description: 'Custom security validation rules',
487
-
488
- // Optional: Configuration schema for validation
489
- configSchema: {
490
- type: 'object',
491
- properties: {
492
- strictMode: { type: 'boolean', default: false },
493
- allowedNamespaces: { type: 'array', items: { type: 'string' } },
494
- },
495
- },
496
-
497
- async validate(manifests: unknown[], context: ValidationContext): Promise<PolicyViolation[]> {
498
- const violations: PolicyViolation[] = [];
499
- const config = context.config as { strictMode?: boolean; allowedNamespaces?: string[] };
500
-
501
- for (const manifest of manifests) {
502
- if (manifest.kind === 'Deployment') {
503
- // Validate security context
504
- if (!manifest.spec?.template?.spec?.securityContext) {
505
- violations.push({
506
- plugin: this.name,
507
- severity: config?.strictMode ? 'error' : 'warning',
508
- message: 'Deployment should specify securityContext',
509
- resourcePath: `${manifest.kind}/${manifest.metadata?.name}`,
510
- field: 'spec.template.spec.securityContext',
511
- suggestion: 'Add securityContext with runAsNonRoot: true',
512
- context: {
513
- kubernetesVersion: context.kubernetesVersion,
514
- environment: context.environment,
515
- },
516
- });
517
- }
518
-
519
- // Validate namespace restrictions
520
- const namespace = manifest.metadata?.namespace || 'default';
521
- if (config?.allowedNamespaces && !config.allowedNamespaces.includes(namespace)) {
522
- violations.push({
523
- plugin: this.name,
524
- severity: 'error',
525
- message: `Deployment in unauthorized namespace: ${namespace}`,
526
- resourcePath: `${manifest.kind}/${manifest.metadata?.name}`,
527
- field: 'metadata.namespace',
528
- suggestion: `Deploy to allowed namespaces: ${config.allowedNamespaces.join(', ')}`,
529
- });
530
- }
531
- }
532
- }
533
-
534
- return violations;
310
+ async validate(manifests) {
311
+ return manifests.flatMap((manifest) => {
312
+ if (!manifest || typeof manifest !== 'object') return [];
313
+
314
+ const object = manifest as {
315
+ kind?: string;
316
+ metadata?: { name?: string };
317
+ };
318
+
319
+ if (object.metadata?.name) return [];
320
+
321
+ return [
322
+ {
323
+ plugin: 'require-metadata-name',
324
+ severity: 'error' as const,
325
+ message: `${object.kind ?? 'Resource'} must have metadata.name`,
326
+ },
327
+ ];
328
+ });
535
329
  },
536
330
  };
537
- ```
538
331
 
539
- ### Advanced Configuration
540
-
541
- ```typescript
542
332
  const policyEngine = new PolicyEngine({
543
- // Execution settings
544
- timeout: 15000, // 15 second timeout per plugin
545
- parallel: true, // Run plugins in parallel for better performance
546
- failFast: false, // Collect all violations before failing
547
- gracefulDegradation: true, // Continue on plugin failures
548
-
549
- // Performance optimization
550
- cacheOptions: {
551
- maxSize: 1000, // Cache up to 1000 validation results
552
- ttl: 300000, // 5 minute cache TTL
553
- enableStats: true, // Enable cache performance monitoring
554
- },
555
-
556
- // Parallel execution tuning
557
- parallelOptions: {
558
- maxConcurrency: 4, // Run up to 4 plugins concurrently
559
- enableResourceMonitoring: true,
560
- },
333
+ timeout: 5000,
334
+ parallel: true,
335
+ });
561
336
 
562
- // Retry configuration
563
- retryConfig: {
564
- maxAttempts: 3,
565
- baseDelay: 1000,
566
- retryOnTimeout: true,
567
- retryOnPluginError: false,
568
- },
337
+ await policyEngine.use(requireMetadataName);
569
338
 
570
- // Plugin-specific configuration
571
- pluginConfig: {
572
- 'security-plugin': {
573
- strictMode: true,
574
- allowedNamespaces: ['default', 'kube-system'],
575
- securityContext: {
576
- required: true,
577
- runAsNonRoot: true,
578
- },
579
- },
580
- 'best-practices-plugin': {
581
- enforceResourceLimits: true,
582
- requireLabels: ['app', 'version', 'environment'],
583
- maxReplicas: 50,
584
- },
585
- 'aws-plugin': {
586
- region: 'us-west-2',
587
- enforceTagging: true,
588
- costOptimization: {
589
- enabled: true,
590
- maxInstanceSize: 'xlarge',
591
- },
592
- },
593
- },
339
+ const chart = new Rutter({
340
+ meta: { name: 'validated', version: '1.0.0' },
341
+ policyEngine,
594
342
  });
595
-
596
- // Register plugins
597
- await policyEngine.use(securityPolicies);
598
- await policyEngine.use(bestPracticesPolicies);
599
- await policyEngine.use(awsPolicies);
600
343
  ```
601
344
 
602
- ### Environment-Specific Policies
345
+ Policy options include plugin timeouts, retries, graceful degradation, caching, parallel execution,
346
+ inline plugin configuration, schema validation, and environment-variable configuration.
603
347
 
604
- ```typescript
605
- // Load different policies based on environment
606
- const createPolicyEngine = (environment: string) => {
607
- const engine = new PolicyEngine({
608
- environment,
609
- configurationLoader: {
610
- configurationFiles: ['config/policy-engine.json', `config/environments/${environment}.json`],
611
- },
612
- });
348
+ `ConfigurationLoader` currently accepts file-related options, but file-backed policy configuration
349
+ loading is not implemented. Do not rely on `configurationFiles` to load plugin configuration from
350
+ disk in the current release.
613
351
 
614
- // Base security policies for all environments
615
- await engine.use(baseSecurity);
616
-
617
- // Environment-specific policies
618
- switch (environment) {
619
- case 'production':
620
- await engine.use(strictSecurity);
621
- await engine.use(compliancePolicies);
622
- await engine.use(awsPolicies);
623
- break;
624
- case 'staging':
625
- await engine.use(moderateSecurity);
626
- await engine.use(awsPolicies);
627
- break;
628
- case 'development':
629
- // Minimal policies for development
630
- await engine.use(basicSecurity);
631
- break;
632
- }
633
-
634
- return engine;
635
- };
636
- ```
352
+ ## Environment variable configuration
637
353
 
638
- ### Integration with CI/CD
354
+ The environment-variable loader reads YAML/JSON configuration and generates Kubernetes `env`
355
+ entries:
639
356
 
640
357
  ```typescript
641
- // In your CI/CD pipeline
642
- import { Rutter, PolicyEngine, PolicyEngineError } from 'timonel';
643
-
644
- const validateChart = async (chartPath: string, environment: string) => {
645
- const policyEngine = await createPolicyEngine(environment);
646
-
647
- try {
648
- const chart = new Rutter({
649
- meta: { name: 'my-app', version: process.env.VERSION },
650
- policyEngine,
651
- });
358
+ import { loadAndGenerateEnvVars } from 'timonel';
652
359
 
653
- await chart.write(chartPath);
654
-
655
- // Log validation success with metrics
656
- const stats = policyEngine.getCacheStats();
657
- console.log('✅ Chart validation passed', {
658
- environment,
659
- cacheHitRate: stats.hitRate,
660
- pluginCount: policyEngine.getPluginCount(),
661
- });
662
- } catch (error) {
663
- if (error instanceof PolicyEngineError) {
664
- console.error('❌ Policy violations found:');
665
-
666
- // Group violations by severity
667
- const errors = error.violations.filter((v) => v.severity === 'error');
668
- const warnings = error.violations.filter((v) => v.severity === 'warning');
669
-
670
- if (errors.length > 0) {
671
- console.error(`\n🚨 Errors (${errors.length}):`);
672
- errors.forEach((v) => {
673
- console.error(` • ${v.resourcePath}: ${v.message}`);
674
- if (v.suggestion) {
675
- console.error(` 💡 ${v.suggestion}`);
676
- }
677
- });
678
- }
679
-
680
- if (warnings.length > 0) {
681
- console.warn(`\n⚠️ Warnings (${warnings.length}):`);
682
- warnings.forEach((v) => {
683
- console.warn(` • ${v.resourcePath}: ${v.message}`);
684
- });
685
- }
686
-
687
- // Fail CI/CD on errors, but allow warnings
688
- if (errors.length > 0) {
689
- process.exit(1);
690
- }
691
- } else {
692
- throw error;
693
- }
694
- }
695
- };
696
-
697
- // Usage in GitHub Actions, GitLab CI, etc.
698
- await validateChart('./dist', process.env.ENVIRONMENT || 'development');
360
+ const env = loadAndGenerateEnvVars({
361
+ configPath: './env-config.yaml',
362
+ defaultScope: 'global.env',
363
+ });
699
364
  ```
700
365
 
701
- ### Plugin Ecosystem
366
+ It can generate literal Helm values and Kubernetes `secretKeyRef` entries. It does not fetch values
367
+ from Vault, AWS Secrets Manager, or other external secret stores.
702
368
 
703
- The Policy Engine supports a rich ecosystem of plugins for various use cases:
369
+ ## CLI
704
370
 
705
- #### Security & Compliance
371
+ Run the locally installed CLI with `pnpm exec tl` or expose the package binary through your package
372
+ manager.
706
373
 
707
- - **Pod Security Standards** - Kubernetes PSS validation
708
- - **CIS Benchmarks** - Center for Internet Security benchmarks
709
- - **NIST Framework** - NIST Cybersecurity Framework compliance
710
- - **PCI DSS** - Payment Card Industry compliance
711
- - **SOC 2** - Service Organization Control 2 compliance
374
+ ```text
375
+ tl init <chart-name>
376
+ tl synth [chartDir] [outDir]
377
+ tl validate
378
+ tl deploy <release> [namespace]
379
+ tl templates
380
+ tl umbrella init <name>
381
+ tl umbrella add <subchart>
382
+ tl umbrella synth [outDir]
383
+ ```
712
384
 
713
- #### Cloud Provider Integrations
385
+ Common flags:
714
386
 
715
- - **AWS Well-Architected** - AWS best practices and cost optimization
716
- - **Azure Security Center** - Azure-specific security policies
717
- - **GCP Security Command Center** - Google Cloud security validation
387
+ ```text
388
+ --dry-run
389
+ --silent
390
+ --env <environment>
391
+ --set <key=value>
392
+ --mode <dependencies|inline>
393
+ --help, -h
394
+ ```
718
395
 
719
- #### Development & Operations
396
+ `--dry-run` validates the requested operation and reports what would happen without writing chart
397
+ files or invoking Helm. Combine it with `--silent` when the preview itself should also be suppressed.
720
398
 
721
- - **GitOps Policies** - GitOps workflow validation
722
- - **Resource Optimization** - Cost and performance optimization
723
- - **Observability** - Monitoring and logging best practices
724
- - **Backup & Recovery** - Data protection policies
399
+ `tl validate` and `tl deploy` execute the Helm CLI, so Helm must be installed and the current working
400
+ directory must point at the chart you intend to validate or deploy.
725
401
 
726
- #### Creating Plugin Packages
402
+ ## Generated chart structure
727
403
 
728
- ```typescript
729
- // package.json for a policy plugin
730
- {
731
- "name": "@mycompany/k8s-security-policies",
732
- "version": "1.0.0",
733
- "description": "Security policies for Kubernetes manifests",
734
- "main": "dist/index.js",
735
- "types": "dist/index.d.ts",
736
- "keywords": ["timonel", "policy", "security", "kubernetes"],
737
- "peerDependencies": {
738
- "timonel": "^3.1.0"
739
- }
740
- }
404
+ A normal chart written by `Rutter` looks like:
741
405
 
742
- // src/index.ts
743
- export { SecurityPlugin } from './security-plugin.js';
744
- export { CompliancePlugin } from './compliance-plugin.js';
745
- export type { SecurityConfig, ComplianceConfig } from './types.js';
406
+ ```text
407
+ my-app/
408
+ ├── Chart.yaml
409
+ ├── values.yaml
410
+ ├── values-production.yaml # when envValues.production exists
411
+ ├── .helmignore
412
+ └── templates/
413
+ ├── _helpers.tpl
414
+ ├── App.yaml
415
+ ├── AppHpa.yaml
416
+ └── ...
746
417
  ```
747
418
 
748
- ### Performance & Monitoring
419
+ Resource filenames use stable cdk8s construct identifiers where possible.
749
420
 
750
- The Policy Engine includes comprehensive performance monitoring:
421
+ ## Documentation
751
422
 
752
- ```typescript
753
- // Monitor policy engine performance
754
- const result = await policyEngine.validate(manifests, { name: 'example-chart', version: '1.0.0' });
755
-
756
- console.log('Validation Performance:', {
757
- executionTime: result.metadata.executionTime,
758
- pluginCount: result.metadata.pluginCount,
759
- manifestCount: result.metadata.manifestCount,
760
- violationsFound: result.violations.length,
761
- });
423
+ The GitHub wiki contains the full documentation set:
762
424
 
763
- // Cache performance monitoring
764
- const cacheStats = policyEngine.getCacheStats();
765
- console.log('Cache Performance:', {
766
- hitRate: cacheStats.hitRate,
767
- totalHits: cacheStats.hits,
768
- totalMisses: cacheStats.misses,
769
- cacheSize: cacheStats.size,
770
- });
425
+ - [Home](https://github.com/KenkoGeek/timonel/wiki)
426
+ - [Quick Start](https://github.com/KenkoGeek/timonel/wiki/Quick-Start)
427
+ - [Architecture](https://github.com/KenkoGeek/timonel/wiki/Architecture)
428
+ - [API Reference](https://github.com/KenkoGeek/timonel/wiki/API-Reference)
429
+ - [Type-Safe Helm Helpers](https://github.com/KenkoGeek/timonel/wiki/Helm-Helpers-System)
430
+ - [Umbrella Charts](https://github.com/KenkoGeek/timonel/wiki/Examples-Umbrella-Charts)
431
+ - [AWS Resources](https://github.com/KenkoGeek/timonel/wiki/AWS-Resources)
432
+ - [Policy Engine](https://github.com/KenkoGeek/timonel/wiki/Policy-Engine)
433
+ - [CLI Reference](https://github.com/KenkoGeek/timonel/wiki/CLI-Reference)
434
+ - [Migration Guide](https://github.com/KenkoGeek/timonel/wiki/Migration-Guide)
435
+ - [Contributing](https://github.com/KenkoGeek/timonel/wiki/Contributing)
436
+ - [Release and Versioning](https://github.com/KenkoGeek/timonel/wiki/Release-and-Versioning)
771
437
 
772
- // Clear cache when needed
773
- policyEngine.invalidateCache({ all: true });
774
- ```
438
+ ## Development
775
439
 
776
- ## 📚 Documentation
777
-
778
- - **[API Reference](https://github.com/KenkoGeek/timonel/wiki/API-Reference)** - Complete API
779
- documentation
780
- - **[CLI Reference](https://github.com/KenkoGeek/timonel/wiki/CLI-Reference)** - Command-line
781
- interface guide
782
- - **[Policy Engine Guide](https://github.com/KenkoGeek/timonel/wiki/Policy-Engine)** -
783
- Policy validation and plugin development
784
- - **[Plugin Development Guide](https://github.com/KenkoGeek/timonel/wiki/Plugin-Development)** -
785
- Creating custom policy plugins
786
- - **[Configuration Reference](https://github.com/KenkoGeek/timonel/wiki/Policy-Configuration)** -
787
- Policy engine configuration options
788
- - **[Policy Examples](https://github.com/KenkoGeek/timonel/wiki/Policy-Examples)** - Example plugins
789
- and usage patterns
790
- - **[Examples](https://github.com/KenkoGeek/timonel/wiki/Examples)** - Real-world usage examples
791
- - **[Best Practices](https://github.com/KenkoGeek/timonel/wiki/Best-Practices)** - Recommended
792
- patterns and practices
793
- - **[Contributing](https://github.com/KenkoGeek/timonel/wiki/Contributing)** - Development setup
794
- and guidelines
795
- - **[Timonel Examples Repository](https://github.com/KenkoGeek/timonel-examples)** - Curated
796
- collection of ready-to-run Timonel sample projects (Outdated)
797
-
798
- ## 🔧 Troubleshooting
799
-
800
- ### CDK8s Module Not Found Error
801
-
802
- If you get `Error: Cannot find module 'cdk8s'` when running `tl umbrella synth`:
803
-
804
- **Problem**: Timonel is installed globally, but your project needs CDK8s dependencies locally.
805
-
806
- **Solution**: Create a `package.json` in your project directory:
807
-
808
- ```json
809
- {
810
- "name": "my-timonel-project",
811
- "version": "1.0.0",
812
- "type": "module",
813
- "dependencies": {
814
- "cdk8s": "^2.70.28",
815
- "cdk8s-plus-33": "^2.4.6",
816
- "constructs": "^10.4.3",
817
- "timonel": "^3.1.0"
818
- },
819
- "devDependencies": {
820
- "@types/node": "^24.5.2",
821
- "typescript": "^5.9.2"
822
- }
823
- }
440
+ ```bash
441
+ pnpm install --frozen-lockfile
442
+ pnpm ci:check
443
+ pnpm test:unit
444
+ pnpm test:integration
445
+ pnpm test:coverage
446
+ pnpm md:lint
447
+ pnpm doc:coverage:validate
448
+ pnpm security:audit
449
+ pnpm pack
824
450
  ```
825
451
 
826
- Then run:
452
+ Contribution and agent rules are defined in [`AGENTS.md`](AGENTS.md).
827
453
 
828
- ```bash
829
- npm install
830
- tl umbrella synth # Now it works!
831
- ```
454
+ ## Releases
455
+
456
+ Development follows a trunk-based model centered on `main`:
832
457
 
833
- ## 🤝 Contributing
458
+ - pull requests target `main`;
459
+ - successful main CI can publish an npm `canary` build;
460
+ - stable publication is an explicit workflow with production approval;
461
+ - npm publication uses Trusted Publishing/OIDC and provenance.
834
462
 
835
- See our [Contributing Guide](https://github.com/KenkoGeek/timonel/wiki/Contributing) for development
836
- setup and guidelines.
463
+ See the [release guide](https://github.com/KenkoGeek/timonel/wiki/Release-and-Versioning) for the
464
+ current workflow and operational constraints.
837
465
 
838
- ## 📄 License
466
+ ## License
839
467
 
840
468
  MIT
841
469
 
@@ -849,13 +477,13 @@ MIT
849
477
  [security-url]: SECURITY.md
850
478
  [pnpm-badge]: https://img.shields.io/badge/pm-pnpm-ffd95a?logo=pnpm&logoColor=fff&labelColor=24292e
851
479
  [pnpm-url]: https://pnpm.io/
852
- [node-badge]: https://img.shields.io/badge/node-%3E%3D22-339933?logo=node.js&logoColor=fff
480
+ [node-badge]: https://img.shields.io/badge/node-22.22.2%20%7C%2024.15%20%7C%2026-339933?logo=node.js&logoColor=fff
853
481
  [node-url]: https://nodejs.org/
854
- [ts-badge]: https://img.shields.io/badge/TypeScript-5.x-3178C6?logo=typescript&logoColor=fff
482
+ [ts-badge]: https://img.shields.io/badge/TypeScript-6.x-3178C6?logo=typescript&logoColor=fff
855
483
  [ts-url]: https://www.typescriptlang.org/
856
484
  [maintained-badge]: https://img.shields.io/badge/maintained%20by-KenkoGeek-6C78AF?style=flat
857
485
  [maintained-url]: https://github.com/kenkogeek/
858
- [ci-badge]: https://github.com/KenkoGeek/timonel/actions/workflows/test.yaml/badge.svg?branch=main
859
- [ci-url]: https://github.com/KenkoGeek/timonel/actions/workflows/test.yaml
486
+ [ci-badge]: https://github.com/KenkoGeek/timonel/actions/workflows/ci.yml/badge.svg?branch=main
487
+ [ci-url]: https://github.com/KenkoGeek/timonel/actions/workflows/ci.yml
860
488
  [codeql-badge]: https://github.com/KenkoGeek/timonel/actions/workflows/codeql.yml/badge.svg
861
489
  [codeql-url]: https://github.com/KenkoGeek/timonel/actions/workflows/codeql.yml