Design
Launcher — Design Document
Date: 2026-04-19 | Updated: 2026-10-04 | Status: Phase 3 shipped, Phase 5 gate met
| Version | Date | Summary |
|---|---|---|
| 1.11 | 2026-10-04 | §11 “Launcher Layout” deleted with the kurel build --oci-repository layer it described (go-kure/launcher#781); the later sections keep their numbers. §12 names who owns delivery |
| 1.10 | 2026-10-04 | §11 marked superseded by docs/delivery-scope.md (delivery-agnostic scope) |
| 1.9 | 2026-10-02 | §9.1: the document-format lifecycle takes effect at the first stable GA release; before it, no format stability promise |
| 1.8 | 2026-09-30 | §11: kurel build --oci-repository generates the Launcher Layout (per-bundle artifacts, OCIRepository and Kustomization) |
| 1.7 | 2026-09-23 | Record the pre-release bug-fix exception to the document-format lifecycle in §9.1 |
| 1.6 | 2026-08-23 | Record the type-name reservation covenant and document-format lifecycle decisions in §9.1 |
| 1.5 | 2026-08-23 | Record the pkg/patch disposition in §9.1; note the pkg/launcher removal in §2; retitle the §5 patch row |
| 1.4 | 2026-08-22 | Status refresh: Phase 1b and Phase 3 marked complete, Phase 5 noted gate-met; fix §5 kure-package paths and §13 cross-repo issue refs |
| 1.3 | 2026-05-15 | Replace patch-centric §4 with OAM-native pipeline; add OAM model, Policy interface, launcher layout, and what-launcher-does-not-do sections; update roadmap |
| 1.2 | 2026-05-14 | Record all Phase 0 decisions; add §9 decisions table; update roadmap; trim open questions |
| 1.1 | 2026-05-14 | Update GVK, roadmap, and open questions for second design iteration |
| 1.0 | 2026-04-19 | Initial 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:
- 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.
- 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/andpkg/cmd/kurel/— the CLI entry point and commands
pkg/launcher/ has since been removed; pkg/patch/ was retained as a standalone library
(see §9.1).
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
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
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.
| Concern | Lives in |
|---|---|
| K8s resource construction | kure (pkg/kubernetes) |
| GitOps engine (FluxCD, ArgoCD) | kure (pkg/stack/fluxcd, pkg/stack/argocd) |
| Kubernetes resource builders for CRD operators | kure (pkg/kubernetes/certmanager, etc.) |
| OAM package format and runtime | launcher |
| Parameter resolution | launcher (pkg/oam, ResolveParameters) |
| Post-generation patch application (standalone library, not part of the build pipeline) | launcher (pkg/patch) |
| Two-set parameter model (platform + app) | launcher |
| CLI tool | launcher |
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 go-kure/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.
| Package | Why not used |
|---|---|
pkg/stack/generators/ (AppWorkload, FluxHelm, KurelPackage) | kure’s pre-built generator types; consumed only by kure’s own tests, not by launcher or its downstream consumers |
pkg/stack/application_wrapper.go (ApplicationWrapper) | kure’s GVK YAML dispatch mechanism; launcher defines its own OAM parser under launcher.gokure.dev/v1alpha1 |
pkg/gvk | kure’s generic GVK registry; not relevant to launcher’s static-file OAM model |
generators.gokure.dev/*, stack.gokure.dev/* API groups | kure’s versioning space; launcher’s documents use launcher.gokure.dev exclusively |
What launcher does use from kure (intentional)
| Package | Purpose |
|---|---|
pkg/stack.ApplicationConfig | Handler output interface — ComponentHandler.ToApplicationConfig() in pkg/oam/handler.go returns stack.ApplicationConfig, which is the bridge to kure’s resource builders |
pkg/kubernetes | Kubernetes resource construction (Deployment, Service, etc.) |
pkg/kubernetes/fluxcd | GitOps delivery object generation (OCIRepository, Kustomization, HelmRelease, and the HelmRepository, GitRepository, Bucket and HelmChart sources) — used by the oci and helmrelease component handlers and the kind-named Flux source handlers (helmrepository, ocirepository, gitrepository, bucket, helmchart) |
pkg/manifest | CRD / manifest classification — used by the crd and manifests component handlers |
pkg/stack/helm | Helm chart handling — used by the helmtemplate component handler |
pkg/stack/layout | GitOps repo layout — used by trait decorators |
pkg/errors | Error types |
pkg/io | YAML parsing utilities |
pkg/logger | Logging interface |
6. Comparison with Helm
| Aspect | Helm | Launcher / Kurel |
|---|---|---|
| Package format | Go templates + values.yaml | OAM Application spec + typed parameters |
| Platform vs app config | Single values.yaml (no separation) | Two explicit parameter sets |
| Semantics | Arbitrary YAML generation | OAM components/traits (typed intent) |
| Platform customization | Via values + conditional templates | Via platform profile (trait implementation resolution) |
| Output | Manifest apply to cluster | Static manifests → GitOps delivery |
| Cluster runtime component | Tiller (Helm 2) / none (Helm 3) | None — compile-time only |
| Composability | Helm subcharts | Multi-component Application model; optional/multi-instance components and split files deferred to Phase 2 (go-kure/launcher#39); standalone post-generation patching (pkg/patch) is a separate, opt-in step |
7. Comparison with KubeVela
KubeVela is the reference OAM runtime. The key architectural difference:
| Aspect | KubeVela | Launcher / Kurel |
|---|---|---|
| Runtime model | Live reconciler (CRD controller in cluster) | Compiler (offline, static output) |
| Cluster dependency | Requires KubeVela CRDs installed | No cluster-side component |
| Audit trail | Live CRD state | Git history of generated manifests |
| GitOps | Via VelaUX addon or GitOps integration | Native — 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.
| Type | apiVersion | Purpose |
|---|---|---|
Application | launcher.gokure.dev/v1alpha1 | Bundle of components + traits to deploy |
Package | launcher.gokure.dev/v1alpha1 | Kurel package metadata and parameter schema |
ClusterProfile | launcher.gokure.dev/v1alpha1 | Platform 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 go-kure/launcher#58 closed issues go-kure/launcher#36 (package spec), go-kure/launcher#37 (ClusterProfile), go-kure/launcher#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.
| Concern | Decision | Document |
|---|---|---|
| API group and version | launcher.gokure.dev/v1alpha1 for all native docs | design-gvk.md |
| Parser strictness | Strict — unknown fields are a build error | design-gvk.md |
| ClusterProfile format | cluster.yaml carries rendering only; no parameters | design-cluster-profile.md |
| Parameter syntax | Option A — ${var} placeholders; typed schema in kurel.yaml | options-param-syntax.md |
| Policy interface | Option A — typed accessor interface (23 methods) | options-policy-interface.md |
| Package composition | Deferred to Phase 2 — no optional sections in Phase 1 | options-package-composition.md |
9.1 Later Design Decisions
| Concern | Decision | Document |
|---|---|---|
| Capability rendering schema | Typed Go struct per handler + reflection-derived JSON Schema; ValidateAndApplyDefaults interface; custom CapabilityDefinition document kind deferred to Phase 2/3 | design-capability-schema.md |
pkg/patch disposition | Keep as a supported, standalone library for post-generation resource modification (JSONPath ops, TOML/YAML patch dialects, strategic-merge patch, conflict detection). Deliberately not part of the kurel build pipeline — that pipeline is closed through explicit registration (parameters plus registered component, trait, and lowering-rule handlers), not universally typed. Designated implementation for interactive patch mode (go-kure/launcher#18); not a conditional-composition mechanism (go-kure/launcher#39 does not depend on it) | pkg/patch/README.md |
| Type-name reservation | Covenant: a type name in use by a dialect extending launcher’s model is reserved; launcher takes it only by upstreaming the feature with the same semantics; successor names (<family>.v<N>) reserve the whole family; enforcement is review discipline, not CI | design-gvk.md |
| Document-format lifecycle | Before the first stable GA release, no stability promise: fixes, features and removals may change or newly reject documents under launcher.gokure.dev/v1alpha1, signalled by a format changelog entry; from that release on, additive-only under one long-lived string (same test: existing docs stay valid and compile the same), and a breaking change moves the apiVersion; no in-document format counter | design-gvk.md |
10. Policy Interface
The Policy interface is launcher’s primary extension point for downstream consumers to enforce environment-specific constraints without modifying launcher’s built-in handlers.
NoopPolicy is the default when no policy document is supplied: no enforced limits, no defaults applied, security-sensitive boolean flags default-deny (false) — except the two container-capability accessors, which are default-allow (nil means unconstrained, go-kure/launcher#305). Downstream consumers implement Policy (typically via their own EnvironmentPolicy type) to add enforcement. See docs/oam/options-policy-interface.md for the full design rationale.
12. What Launcher Does NOT Do
| Concern | Who owns it |
|---|---|
Delivery of an application: the objects that apply it (Flux Kustomizations and their sources), health checks, reconciliation settings, patches | the consumer that delivers it; see Launcher delivery scope |
| Multi-OCI artifact splitting (per-app, per-layer) | a downstream delivery runtime (multi-layer Flux hierarchy) |
| Platform component catalog | a downstream platform runtime + component registry |
| Environment-specific enforcement (registry allowlist, replica limits) | a downstream policy consumer (EnvironmentPolicy implements Policy) |
| Cluster provisioning and lifecycle | a downstream cluster-lifecycle tool |
| 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 (go-kure/launcher#36, go-kure/launcher#37, go-kure/launcher#38)
- Clean up prototype pipeline and align CLI with OAM design — done (go-kure/launcher#40, go-kure/launcher#41, go-kure/launcher#42)
Phase 1: OAM Core (go-kure/launcher#31)
pkg/oam: types, parser, validator (go-kure/launcher#43)pkg/oam: handler interfaces (ComponentHandler, TraitHandler, CapabilityAware) (go-kure/launcher#44)pkg/oam: ClusterProfile and CapabilityBinding types (go-kure/launcher#45)pkg/oam: Policy interface, Enforceable interface, NoopPolicy (go-kure/launcher#46)pkg/oam: OAM runtime skeleton (Transformer, handler registry, TransformContext) (go-kure/launcher#47)pkg/oam: transform pipeline (capability wiring, policy application) (go-kure/launcher#53)- Fixture parity and regression coverage (go-kure/launcher#54)
Phase 1b / Vertical Slice (complete): First end-to-end kurel build (go-kure/launcher#56)
- One component + one trait → working manifest output
- Validates the OAM parser → handler → kure pipeline end-to-end
Phase 2: Built-in handlers (go-kure/launcher#32)
- Component handlers: webservice, worker, deployment, postgresql, cronjob, job, helmrelease, helmtemplate, daemonset, statefulset, service (go-kure/launcher#48, go-kure/launcher#343, go-kure/launcher#344, go-kure/launcher#411, go-kure/launcher#327, go-kure/launcher#348)
- Trait handlers — workload set: expose, certificate, external-secret, pvc, scaler (go-kure/launcher#49)
- Trait handlers — network/infra set: ingress, httproute, configmap, networkpolicy, cilium-networkpolicy, volsync (go-kure/launcher#50)
- Generic
passthroughcomponent — emits arbitrary CRDs / non-standard objects with no per-type Go handler (go-kure/launcher#105) - Manifest-source components:
crd,manifests— emit CRDs / arbitrary manifests from inline or http(s) URL sources, with scope-aware namespace stamping (classifier in kurepkg/manifest); for content authored as YAML rather than as typed component properties ocisource component — emits anOCIRepositorysource CR (URL+version dedup, flux-namespace placement) plus a per-component FluxKustomization; for content already published as an OCI artifact, reconciled by Flux- Kind-named Flux source components:
helmrepository,ocirepository,gitrepository,bucket— each a strict 1:1 projection of its source-controller spec, emitting exactly one source CR (no dedup, flux-namespace placement, fetch host constrained by the policy registry allowlist) (go-kure/launcher#347) - Kind-named
helmchartcomponent — the FluxHelmChartsource, a strict 1:1 projection ofHelmChartSpecemitting exactly one CR in the Flux namespace; it reuses the name of the removedhelmchartcomposite, whose top-level keys get a hint pointing tohelm(go-kure/launcher#351) - Kind components for the objects the workload kinds generate for themselves:
serviceaccount,persistentvolumeclaim,configmap. Each emits exactly one object named after the component. Apvcvolume’sclaimNamemounts an existing claim without generating one.webserviceandworkeremit their ServiceAccount as aserviceaccountsibling member and their described claims as synthesizedpvctraits, with byte-identical output; the five pod kinds (deployment,statefulset,daemonset,cronjob,job) generate no ServiceAccount and no claim, and theirpvcvolumes requireclaimName(go-kure/launcher#702) - Role-named
helmcomponent — a component-position lowering rule ontohelmrelease(plus a generatedhelmrepository/ocirepository, shared per URL within an application and applied with the application bundle, ahead of its ordered groups) orhelmtemplate; the successor to the removedhelmchartcomposite (go-kure/launcher#349, go-kure/launcher#350)
Phase 3 (complete): CLI integration (go-kure/launcher#33)
kurel build— OAM mode (app.yaml + –profile cluster.yaml → manifests) (go-kure/launcher#51)
Phase 4: Downstream integration (go-kure/launcher#34) — deferred
- a downstream runtime becomes a launcher consumer; EnvironmentPolicy, handler migration
Phase 5: OCI distribution (go-kure/launcher#35) — gate met, not yet started
- OCI-based package publishing and pulling
- Package catalog as OCI index (go-kure/launcher#16)
14. Open Questions
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 go-kure/launcher#39).
Generic/raw YAML escape hatch — Implemented as the
passthroughcomponent (go-kure/launcher#105): it emits an arbitrary Kubernetes object declared inline underobject:, with aclusterScopedflag for cluster-scoped resources, covering cases outside the built-in handler set. Config-driven custom traits and template/plugin rendering remain future work (go-kure/launcher#102).Package distribution — How kurel packages are published, versioned, and referenced by consumers.
kurel buildruns today against a standard local layout without extra flags (Phase 3, complete); OCI-based publishing and pulling is Phase 5, gate met but not yet started.