Skip to content

Flux Factories ​

Factory functions for Flux CD Custom Resource Definitions.

Import ​

typescript
// Helm factories from main package
import { helmRelease, helmRepository } from 'typekro';

// Source factories from subpath
import { gitRepository } from 'typekro/flux';

Quick Example ​

typescript
import { helmRelease, helmRepository } from 'typekro';

const repo = helmRepository({
  name: 'bitnami',
  url: 'https://charts.bitnami.com/bitnami',
  id: 'bitnami'
});

const release = helmRelease({
  name: 'redis',
  chart: { 
    repository: 'https://charts.bitnami.com/bitnami', 
    name: 'redis' 
  },
  values: { replica: { replicaCount: 3 } },
  id: 'redis'
});

Available Factories ​

FactoryImportDescription
helmReleasetypekroDeploy Helm charts via Flux
helmRepositorytypekroHelm chart repository source
HelmCharttypekro/simpleSimplified Helm deployment
gitRepositorytypekro/fluxGit repository source

helmRelease() ​

Full HelmRelease configuration for deploying Helm charts.

typescript
import { helmRelease } from 'typekro';

const release = helmRelease({
  id: 'myApp',
  name: 'my-app',
  namespace: 'production',
  interval: '10m',
  chart: {
    repository: 'https://charts.example.com',
    name: 'my-chart',
    version: '1.2.3'
  },
  values: {
    replicaCount: 3,
    image: { tag: 'v1.0.0' }
  },
  valuesFrom: [{
    kind: 'Secret',
    name: 'my-app-values',
    valuesKey: 'databaseUrl',
    targetPath: 'config.databaseUrl'
  }],
  postRenderers: [{
    kustomize: {
      patches: [{
        target: { group: 'apps', version: 'v1', kind: 'Deployment', name: 'my-app' },
        patch: '- op: remove\n  path: /metadata/annotations/legacy'
      }]
    }
  }]
});

valuesFrom exposes Flux's Secret/ConfigMap value projection without copying the source value into TypeKro state. Its name, valuesKey, and targetPath fields are graph-aware, so resources created or observed in the same composition can provide their names through ordinary TypeKro references. Values projected through targetPath use Helm --set parsing semantics under the managed Flux 2.7.5 baseline. TypeKro intentionally withholds Flux 2.9's literal field until the managed runtime and Kubernetes compatibility baseline can move together. Use chart-native envFrom or mounted files for arbitrary Secret bytes; do not route credentials with punctuation through targetPath.

postRenderers exposes Flux's Kustomize post-rendering boundary for strategic-merge and JSON 6902 patches. Patch targets are graph-aware, so their names and namespaces may come from schema or resource references. Prefer chart-native values for ordinary configuration; use post-rendering when an upstream chart cannot express a required manifest transformation.

Install, upgrade and CRD policy ​

Every TypeKro HelmRelease factory accepts the same three Flux lifecycle options. That covers the generic helmRelease and every integration factory (certManagerHelmRelease, traefikHelmRelease, cnpgHelmRelease, ...):

OptionFields
installtimeout, crds (Skip / Create / CreateReplace), createNamespace, remediation.retries, remediation.remediateLastFailure, remediation.ignoreTestFailures
upgradetimeout, crds, remediation.retries, remediation.remediateLastFailure, remediation.ignoreTestFailures, remediation.strategy
driftDetectionmode (enabled / warn / disabled), ignore

Each field you set overrides the factory's default for that field, and remediation is merged field by field too. driftDetection replaces the default as a whole.

typescript
import { traefikHelmRelease } from 'typekro/traefik';

const release = traefikHelmRelease({
  name: 'traefik',
  install: { timeout: '20m' },
  upgrade: { crds: 'CreateReplace', remediation: { retries: 5 } },
});

With remediation.retries above 0, Flux remediates every failure, the last one included: a failed install is uninstalled and tried again, and a failed upgrade is rolled back (or uninstalled, with strategy: 'uninstall') and tried again. Set retries: 0 for a chart that must never be rolled back automatically.

In KRO mode the options can be schema references, either field by field (install: { timeout: spec.installTimeout }) or as whole objects (install: spec.install). Each field that has a default renders as Cel.default(<instance field>, <default>), so an instance that leaves it unset still gets the factory's default, the same as in direct mode. Fields your schema does not declare keep their defaults and are never read from the instance. A schema field passed as retries must be declared 'number.integer': the HelmRelease takes an integer, and KRO types a plain 'number' as a float. The factory throws a ValidationError at build time for a 'number' field, and in direct mode for a fractional value such as 2.7, so both modes reject what the CRD would. TypeKro never rounds or truncates the value. A reference to another resource (for example a ConfigMap field) gets no fallback: KRO waits for that field as usual.

Unless a factory sets its own policy, it uses the defaults of the generic helmRelease: install.timeout and upgrade.timeout of 10m, and 3 remediation retries for each. Without them Flux uses a 5m timeout and no retries, so a slow first install stays Stalled until someone runs flux reconcile helmrelease <name> --reset. These factories set their own policy:

FactoryDefault
hatchetHelmRelease, harborHelmRelease20m timeouts. Failed upgrades are not retried or rolled back, because they may run database migrations
envoyGatewayHelmRelease and the Envoy AI Gateway releases15m timeouts, remediateLastFailure, rollback when an upgrade fails
traefikHelmReleasecrds: CreateReplace on install and upgrade (from its crds option), rollback when an upgrade fails, timeouts from spec.timeout
apisixHelmReleaseinstall.createNamespace: true, timeouts from spec.timeout
crowdsecHelmReleaseRollback when an upgrade fails, timeouts from spec.timeout (15m, for the registration Jobs and first-start hub downloads), no drift detection
kedaHelmReleaseRollback when an upgrade fails, drift detection enabled, timeouts from spec.timeout (10m). Its CRDs are chart templates (crds.install), so no Flux CRD policy is set
vpaHelmReleasecrds: CreateReplace on install and upgrade, rollback when an upgrade fails, drift detection enabled, timeouts from spec.timeout (10m)
karpenterHelmRelease, karpenterCrdHelmReleasecrds: Skip on install and upgrade for the controller chart, rollback when an upgrade fails, drift detection enabled, timeouts from spec.timeout (10m)
ciliumHelmReleaseTimeouts from its installTimeout and upgradeTimeout options, or from timeout for both. 10m when none is set

Flux's own CRD defaults are Create on install and Skip on upgrade. A chart that ships CRDs in its crds/ directory therefore keeps the CRDs of the version it was first installed at, until you set upgrade.crds: CreateReplace. The CRD policy applies only to files in a chart's crds/ directory. Charts that render their CRDs as templates, such as cert-manager (crds.enabled), ignore it.

A custom integration factory can reuse the same merge through helmReleaseLifecycle from typekro/helm:

typescript
import { helmReleaseLifecycle } from 'typekro/helm';

declare const config: { install?: { timeout?: string } };

// Spread into the HelmRelease spec the factory builds itself.
const lifecycle = helmReleaseLifecycle(config, { driftDetection: { mode: 'enabled' } });

helmRepository() ​

Creates a Flux HelmRepository resource.

typescript
import { helmRepository } from 'typekro';

const repo = helmRepository({
  id: 'bitnamiRepo',
  name: 'bitnami',
  namespace: 'flux-system',
  url: 'https://charts.bitnami.com/bitnami',
  interval: '10m'
});

Type-Safe Helm Values ​

values is a recursive TypeKro value tree. In graph mode, TypeKro walks the object and serializes supported data leaves automatically:

  • primitives, arrays, and plain objects
  • schema and resource magic-proxy references
  • CEL expressions
  • mixed template strings such as `https://${spec.host}`

Unsupported runtime-only leaves are rejected with a path-specific error. Do not put functions, callbacks, clients, symbols, class instances, or live cluster reads inside values; move that logic to a first-class factory field or a direct-only API instead. undefined fields are omitted recursively.

Schema references work naturally in Helm values:

typescript
import { kubernetesComposition, helmRelease } from 'typekro';

const app = kubernetesComposition(definition, (spec) => {
  const release = helmRelease({
    id: 'app',
    name: 'app',
    chart: { repository: 'https://charts.example.com', name: 'myapp' },
    values: {
      replicaCount: spec.replicas,      // Schema reference
      image: { tag: spec.version },     // Nested reference
      config: {
        database: spec.dbName,
        publicUrl: `https://${spec.host}`,
        logLevel: spec.logLevel
      }
    }
  });

  return {
    ready: release.status.conditions?.[0]?.status === 'True'
  };
});

Usage in Compositions ​

typescript
import { type } from 'arktype';
import { kubernetesComposition, helmRelease } from 'typekro';

const DatabaseSpec = type({
  name: 'string',
  size: 'string',
  replicas: 'number'
});

const database = kubernetesComposition({
  name: 'database',
  apiVersion: 'data.example.com/v1',
  kind: 'Database',
  spec: DatabaseSpec,
  status: type({ ready: 'boolean' })
}, (spec) => {
  const db = helmRelease({
    id: 'db',
    name: spec.name,
    chart: {
      repository: 'https://charts.bitnami.com/bitnami',
      name: 'postgresql',
      version: '12.x'
    },
    values: {
      primary: { persistence: { size: spec.size } },
      readReplicas: { replicaCount: spec.replicas }
    }
  });

  return {
    ready: db.status.conditions?.[0]?.status === 'True'
  };
});

Prerequisites ​

HelmRelease requires Flux CD installed in your cluster:

bash
# Install Flux CLI
brew install fluxcd/tap/flux

# Bootstrap Flux
flux bootstrap github \
  --owner=<your-org> \
  --repository=<your-repo> \
  --path=clusters/my-cluster

Or use the TypeKro Runtime Bootstrap to install Flux and Kro together.

Next Steps ​

Released under the Apache 2.0 License.