Design

Launcher — Design Document

Date: 2026-04-19 | Updated: 2026-05-15 | Status: Phase 0 design complete

VersionDateSummary
1.32026-05-15Replace patch-centric §4 with OAM-native pipeline; add OAM model, Policy interface, launcher layout, and what-launcher-does-not-do sections; update roadmap
1.22026-05-14Record all Phase 0 decisions; add §9 decisions table; update roadmap; trim open questions
1.12026-05-14Update GVK, roadmap, and open questions for second design iteration
1.02026-04-19Initial draft

1. Vision

Launcher is an OAM-inspired package manager for Kubernetes — a semantically richer alternative to Helm.

Where Helm templates Kubernetes manifests from Go template files and a flat values.yaml, launcher models deployments using OAM concepts (Applications, Components, Traits) as its package format, under launcher’s own API group. The result is a tool where application structure is explicit and typed, platform implementation choices are separated from application choices, and output is always static, GitOps-ready Kubernetes manifests.

Launcher defines its own native application model under launcher.gokure.dev/v1alpha1. OAM is the conceptual inspiration, not the API contract — launcher does not claim native API compatibility with core.oam.dev/v1beta1.

Launcher uses the kure library for Kubernetes resource generation.


2. Background — Origin in kure

Launcher originated as pkg/launcher inside the kure library — an early prototype of a declarative, file-based Kubernetes manifest generation pipeline. That prototype demonstrated the core idea (parametric package definitions, patch-based composition) but was not yet OAM-aware and did not have a clear separation between platform and application concerns.

The decision to extract launcher into its own repository reflects two things:

  1. Launcher is an application (a CLI tool with its own user, design space, and release cadence), not a library component. It does not belong inside kure.
  2. The design direction is changing: the next iteration is built around OAM rather than the file-based patch pipeline of the prototype.

The following code from kure moved to this repository as the starting point:

  • pkg/launcher/ — the prototype pipeline (loader, resolver, patch processor, validator, builder)
  • pkg/patch/ — the patch engine (TOML/YAML parsing, JSONPath application, strategic merge, conflict detection)
  • cmd/kurel/ and pkg/cmd/kurel/ — the CLI entry point and commands

3. Core Concept: The Kurel Package

A kurel package is a bundle of OAM specs that can be instantiated with parameters. It represents a reusable, shareable application pattern — think “a web application with ingress, TLS, and external secret injection” as a single distributable unit.

3.1 Package Contents

my-webservice/
├── kurel.yaml          # Package metadata and parameter schema
└── app.yaml            # Application template

kurel.yaml is the package’s public API — it declares the parameters (name, type, default, required) and the package version. app.yaml is a launcher Application document. The package author defines what can be varied; consumers fill in the values.

The Application document uses apiVersion: launcher.gokure.dev/v1alpha1, kind Application. ClusterProfile documents (cluster.yaml) and Package documents (kurel.yaml) use the same API group and version. See docs/oam/design-gvk.md for the full GVK rationale.

3.2 Two Parameter Sets

Every kurel package accepts two distinct parameter sets at instantiation time:

Platform profile (set 1)

Describes how the platform implements each trait. These values are environment-specific, not application-specific. A team managing a cluster defines one platform profile; all applications deployed to that cluster share it.

Examples:

  • Which ingress controller is in use (Nginx, Traefik, Gateway API)
  • Which certificate authority backs the cert manager (Let’s Encrypt, internal CA)
  • Which secret store implementation is active (Vault, AWS Secrets Manager, Azure Key Vault via External Secrets)
  • Which GitOps engine is in use (FluxCD, ArgoCD)

Platform profiles express the trait implementation choices made by the platform operator. The OAM spec in the package stays generic (it says “needs ingress”); the platform profile says “ingress on this cluster means a Gateway API HTTPRoute”.

Application values (set 2)

Describes what this specific application instance needs. These are provided per deployment of the package.

Examples:

  • Container image and tag
  • Replica count
  • Resource requests and limits
  • Feature flags
  • External secret names
  • Domain names

3.3 Separation of Concerns

The split between platform profile and application values maps directly onto OAM’s design intent:

  • OAM Components define what workloads exist — application developer concern
  • OAM Traits define what platform capabilities to attach — platform operator concern
  • The trait implementation (how it works) is a platform profile concern, invisible to the application developer

When deploying multiple packages to a cluster, the platform profile is configured once per environment. Each application provides its own values. Platform changes (e.g. switching ingress controllers) update one profile; no individual application spec changes.


4. Architecture

OAM YAML input (app.yaml + cluster.yaml)
        │
        ▼
┌───────────────────┐
│   OAM Parser      │  pkg/oam — parse Application, Component, Trait, Policy
└────────┬──────────┘
         │
         ▼
┌───────────────────┐
│  Handler Registry  │  ComponentHandler + TraitHandler per OAM type
│  + Transformer    │  TransformContext carries ClusterID, Namespace, Policy, Capabilities
└────────┬──────────┘
         │ ApplicationConfig per component
         ▼
┌───────────────────┐
│   kure library    │  github.com/go-kure/kure
│  K8s builders +   │  Deployment, Service, HelmRelease, …
│  GitOps layout    │  pkg/kubernetes + pkg/stack/fluxcd
└────────┬──────────┘
         │
         ▼
  static K8s manifests (GitOps-ready)

Launcher generates static Kubernetes manifests. It does not deploy them. Consumers commit the output to a Git repository and let a GitOps engine (FluxCD) reconcile it into a cluster.


5. Relationship to kure

Launcher is a consumer of kure, not a component of it.

ConcernLives in
K8s resource constructionkure (pkg/kubernetes)
GitOps engine (FluxCD, ArgoCD)kure (pkg/stack/fluxcd, pkg/stack/argocd)
Kubernetes resource builders for CRD operatorskure (pkg/kubernetes/certmanager, etc.)
OAM package format and runtimelauncher
Parameter resolution and patch applicationlauncher (pkg/patch)
Two-set parameter model (platform + app)launcher
CLI toollauncher

kure remains a standalone library with no dependency on launcher. Launcher imports kure. The dependency is one-directional.

What stays in kure

pkg/stack/generators/kurelpackage/ — historically a kure generator that produced kurel package structure from a kure Application. This package is now being removed from kure (unused by all known consumers; removal tracked in kure#539). It does not move to launcher.

What launcher does not use from kure (intentional)

Launcher does not use the following kure packages. This is by design, not an oversight.

PackageWhy not used
pkg/stack/generators/ (AppWorkload, FluxHelm, KurelPackage)kure’s pre-built generator types; consumed only by kure’s own tests, not by crane or launcher
pkg/stack/application_wrapper.go (ApplicationWrapper)kure’s GVK YAML dispatch mechanism; launcher defines its own OAM parser under launcher.gokure.dev/v1alpha1
pkg/gvkkure’s generic GVK registry; not relevant to launcher’s static-file OAM model
generators.gokure.dev/*, stack.gokure.dev/* API groupskure’s versioning space; launcher’s documents use launcher.gokure.dev exclusively

What launcher does use from kure (intentional)

PackagePurpose
pkg/stack.ApplicationConfigHandler output interface — ComponentHandler.ToApplicationConfig() in pkg/oam/handler.go returns stack.ApplicationConfig, which is the bridge to kure’s resource builders
pkg/kubernetesKubernetes resource construction (Deployment, Service, etc.)
pkg/stack/fluxcdGitOps delivery object generation (OCIRepository, Kustomization)
pkg/errorsError types
pkg/ioYAML parsing utilities
pkg/loggerLogging interface
pkg/cmd/sharedCLI infrastructure

6. Comparison with Helm

AspectHelmLauncher / Kurel
Package formatGo templates + values.yamlOAM Application spec + typed parameters
Platform vs app configSingle values.yaml (no separation)Two explicit parameter sets
SemanticsArbitrary YAML generationOAM components/traits (typed intent)
Platform customizationVia values + conditional templatesVia platform profile (trait implementation resolution)
OutputManifest apply to clusterStatic manifests → GitOps delivery
Cluster runtime componentTiller (Helm 2) / none (Helm 3)None — compile-time only
ComposabilityHelm subchartsOAM composition + patches

7. Comparison with KubeVela

KubeVela is the reference OAM runtime. The key architectural difference:

AspectKubeVelaLauncher / Kurel
Runtime modelLive reconciler (CRD controller in cluster)Compiler (offline, static output)
Cluster dependencyRequires KubeVela CRDs installedNo cluster-side component
Audit trailLive CRD stateGit history of generated manifests
GitOpsVia VelaUX addon or GitOps integrationNative — output is GitOps-ready

Launcher targets teams committed to a GitOps-first workflow who want OAM semantics without a cluster-side controller.


8. OAM Model

Launcher defines its own OAM types under launcher.gokure.dev/v1alpha1. These are not CRDs — they are static YAML files consumed by kurel build.

TypeapiVersionPurpose
Applicationlauncher.gokure.dev/v1alpha1Bundle of components + traits to deploy
Packagelauncher.gokure.dev/v1alpha1Kurel package metadata and parameter schema
ClusterProfilelauncher.gokure.dev/v1alpha1Platform trait implementation map + capability list

Launcher does not use core.oam.dev/v1beta1. That API group belongs to KubeVela’s CRD-based runtime. Launcher is a compile-time tool with its own types.


9. Phase 0 Design Decisions

PR #58 closed issues #36 (package spec), #37 (ClusterProfile), #38 (policy interface). These decisions are final.

Decision rule: launcher is a semantically richer Helm alternative. Explicit package API contract and compiler-verified correctness take precedence over YAML validity at rest.

ConcernDecisionDocument
API group and versionlauncher.gokure.dev/v1alpha1 for all native docsdesign-gvk.md
Parser strictnessStrict — unknown fields are a build errordesign-gvk.md
ClusterProfile formatcluster.yaml carries rendering only; no parametersdesign-cluster-profile.md
Parameter syntaxOption A${var} placeholders; typed schema in kurel.yamloptions-param-syntax.md
Policy interfaceOption A — typed accessor interface (~19 methods)options-policy-interface.md
Package compositionDeferred to Phase 2 — no optional sections in Phase 1options-package-composition.md

9.1 Later Design Decisions

ConcernDecisionDocument
Capability rendering schemaTyped Go struct per handler + reflection-derived JSON Schema; ValidateAndApplyDefaults interface; custom CapabilityDefinition document kind deferred to Phase 2/3design-capability-schema.md

10. Policy Interface

The Policy interface is launcher’s primary extension point for downstream consumers (e.g. crane) to enforce environment-specific constraints without modifying launcher’s built-in handlers.

type Policy interface {
    // Enforced limits — nil or empty string means no limit.
    MaxReplicas() *int32
    MaxCPU() string
    MaxMemory() string
    MaxStorageSize() string
    AllowedRegistries() []string
    // Defaults — nil or empty string means leave the OAM value as-is.
    DefaultReplicas() *int32
    DefaultCPURequest() string
    DefaultMemoryRequest() string
    DefaultCPULimit() string
    DefaultMemoryLimit() string
    // Security flags — false is the zero value (default-deny).
    AllowHostNetwork() bool
    AllowPrivileged() bool
    AllowHostPID() bool
    AllowHostIPC() bool
    AllowHostPathVolumes() bool
    // Capability constraints — nil means unconstrained.
    AllowedCapabilities() []string
    ForbiddenCapabilities() []string
    RequiredCapabilities() []string
}

NoopPolicy is the default when no policy document is supplied: no enforced limits, no defaults applied, security-sensitive booleans default-deny (false). Downstream consumers (e.g. crane’s *api.EnvironmentPolicy) implement Policy to add enforcement. See docs/oam/options-policy-interface.md for the full design rationale.


11. Launcher Layout

Launcher’s GitOps output is intentionally simple:

  • One OCI artifact per bundle (all manifests in one layer)
  • One OCIRepository source per bundle
  • One Kustomization per bundle pointing at the OCI artifact

This monolithic layout is owned by launcher and generated as part of kurel build. Downstream consumers (e.g. crane) may implement their own multi-layer delivery hierarchy on top of the same kure FluxCD primitives — that hierarchy is their own concern, not launcher’s.


12. What Launcher Does NOT Do

ConcernWho owns it
Multi-OCI artifact splitting (per-app, per-layer)crane (4-layer Flux hierarchy)
Platform component catalog (PlatformComponent CRD)crane + harbor
Environment-specific enforcement (registry allowlist, replica limits)crane (EnvironmentPolicy implements Policy)
Cluster provisioning and lifecyclebarge
GitOps engine (running in cluster)FluxCD (external)

13. Current Status and Roadmap

Phase 0 (complete): Extraction, design, and housekeeping

  • Move prototype code from kure — done
  • Establish module structure and CI — done
  • Design OAM-native application model (GVK, ClusterProfile, Policy, package spec) — done (#36, #37, #38)
  • Clean up prototype pipeline and align CLI with OAM design — done (#40, #41, #42)

Phase 0b / Vertical Slice (current): First end-to-end kurel build (#56)

  • One component + one trait → working manifest output
  • Validates the OAM parser → handler → kure pipeline end-to-end

Phase 1: OAM Core (#31)

  • pkg/oam: types, parser, validator (#43)
  • pkg/oam: handler interfaces (ComponentHandler, TraitHandler, CapabilityAware) (#44)
  • pkg/oam: ClusterProfile and CapabilityBinding types (#45)
  • pkg/oam: Policy interface, Enforceable interface, NoopPolicy (#46)
  • pkg/oam: OAM runtime skeleton (Transformer, handler registry, TransformContext) (#47)
  • pkg/oam: transform pipeline (capability wiring, policy application) (#53)
  • Fixture parity and regression coverage (#54)

Phase 2: Built-in handlers (#32)

  • Component handlers: webservice, worker, postgresql, cronjob, helmchart, daemonset, statefulset (#48)
  • Trait handlers — workload set: expose, certificate, external-secret, pvc, scaler (#49)
  • Trait handlers — network/infra set: ingress, httproute, configmap, networkpolicy, cilium-networkpolicy, volsync (#50)
  • Generic passthrough component — emits arbitrary CRDs / non-standard objects with no per-type Go handler (#105)
  • Manifest-source components: crd, manifests — emit CRDs / arbitrary manifests from inline or http(s) URL sources, with scope-aware namespace stamping (classifier in kure pkg/manifest); migrated from crane (#237)
  • oci source component — emits an OCIRepository source CR (URL+version dedup, flux-namespace placement) plus a per-component Flux Kustomization; the OAM counterpart to crane’s platform OCI translator (crane#246 / #241)

Phase 3: CLI integration (#33)

  • kurel build — OAM mode (app.yaml + –profile cluster.yaml → manifests) (#51)

Phase 4: Crane integration (#34) — deferred

  • crane becomes a launcher consumer; EnvironmentPolicy, handler migration

Phase 5: OCI distribution (#35)

  • OCI-based package publishing and pulling
  • Package catalog as OCI index (#16)

14. Open Questions

  1. Conditional inclusion syntax — No optional sections in Phase 1. Package authors publish always-on packages; separate packages cover distinct deployment variants. Boolean on/off gates and multi-instance support are Phase 2 (issue #39).

  2. Generic/raw YAML escape hatch — Implemented as the passthrough component (#105): it emits an arbitrary Kubernetes object declared inline under object:, with a clusterScoped flag for cluster-scoped resources, covering cases outside the built-in handler set. Config-driven custom traits and template/plugin rendering remain future work (#102).

  3. Package distribution — How kurel packages are published, versioned, and referenced by consumers. A standard local layout that lets kurel build run without extra flags is the Phase 3 starting point.