v0.7.0 — Early Access
This release ships the first public early-access build of Orkestra. It focuses on the E2E framework, the typed operator toolchain, and making every example runnable end-to-end without manual pre-steps.
E2E Framework
wait:on import declarations — sleep a duration before an import starts; validated at load time, shown inork validate.spec.customOperator: true— skip the Orkestra bundle and Helm install; useork e2eas a pure test harness for any operator (cert-manager, custom controllers, etc.).- Shared Orkestra across imports — one Helm install and one uninstall per suite;
sharedOrkestraprevents namespace deletion from cascading across imports. All sync output suppressed. ork e2e ./...discovery — recursive discovery of e2e files, skips pure aggregators, supports--waitand--skip.--dry-runflag — single file callsork validate;./...lists files with count and first ten.- Helm output suppression — install/uninstall/setup output captured silently; included in error message on failure.
- Bug fixes —
isPureAggregatorinline check,WaitForResourcefor Deployments,checkAllcommand-before-resource ordering.
CLI — ork generate registry
- Registry generation fixed —
generateKatalogwas settingkat.Specbut never callingm.Enabled(), sokat.Enabled()always returned nil.generate.TypeRegistryiterated over an empty map and silently exited on every invocation — no registry file was ever written. TypeRegistryreturns(bool, error)—ensureMainGois now only written when the registry actually produced content.CustomHooksEnabledandConstructorEnabledcorrected — both methods returned== nilinstead of!= nil. Safe for declarative CRDs (all call sites are guarded byDefaultReconcile()) but would have caused panics and wrong behaviour for typed operators.- Output style —
→ generating registry for <module>,✓ pkg/typeregistry/zz_generated_typeregistry.go,○ nothing to generate— matches the rest of the CLI.
CLI — ork init --pack advanced
- Typed examples embedded —
go:embedsilently skips subdirectories containing ago.mod(nested module rule).09-hooks,10-constructor, and11-mixed-operator-patternwere missing from everyork init --pack advancedinvocation. Fixed by renaminggo.mod/go.sum→.txtat embed time. - Transparent extraction —
ork initnow restoresgo.mod.txt→go.mod,go.sum.txt→go.sum, and strips//go:build ignorefrom all extracted.gofiles. Users get a fully compilable project with no manual steps. - Makefile safety net —
make registryandmake buildin typed examples perform the same restoration for users who clone the repo directly.
CLI — path-relative resolution
crdFile,crFiles,setup.apply, and Komposerimports.filesnow resolve relative to the declaring file.ork run -f /any/path/katalog.yamlworks from any directory.- CLI-provided
-fpaths are converted to absolute on intake so downstream resolution is always anchored correctly.
Examples
examples/use-cases/full-stack-app— all six sub-katalogs switched from inlineapiTypes:tocrdFile:; manualkubectl apply -f crd.yamlpre-step eliminated from every walkthrough.03-cross-crd/crd.yamlsplit intocrd-managed-database.yaml+crd-database-backed-app.yaml.06-full-stackuses asetup:block to pull in the managed-database dependency.- Advanced typed examples —
09-hooks(typed Go hooks),10-constructor(custom reconciler), and11-mixed-operator-pattern(dynamic + hooks + constructor together) fully implemented, documented, and e2e-ready. - Typed e2e documented — READMEs for typed examples now explain
--set runtime.image.repository/--set runtime.image.tagsoork e2edeploys the user’s custom image (which contains the generated type registry) instead of the default Orkestra image.
Documentation
pkg/e2edeveloper docs — all four existing reference pages rewritten; three new pages:05-imports.md,06-discovery.md,07-custom-operator.md.- Schema docs —
wait:imports field andspec.customOperatordocumented. - Getting-started — CLI reference table updated;
ork run -fnote on optional-fwhenkatalog.yamlis in the current directory. - Blog —
04-why-i-built-this.md.
pre-v1-alpha — Public Deployment, CRD Health Stability, Multi-Runtime
Public Deployment (deployments/public/)
- Six-cluster public demo: 6 runtimes, 75 CRDs, 1 Control Center aggregating all of them.
- Local motifs extracted for reuse across Katalogs (
replicaset-service,rotating-api-key,rotating-credentials,worker-pool,tls-cert). Each CRD entry imports what it needs; no repeated boilerplate. - All CRDs carry
subresources: status: {}so the runtime can write status back to the API server. make reload— dev-only target that builds runtime + CC images to/tmp/, loads them into theorkestra-playgroundkind cluster, rewritesvalues.yamlwith adev-<timestamp>tag, and re-deploys all six clusters. Never touches the developer’s localorkCLI binary.- Runtime resource limits reduced to 50m/64Mi requests, 200m/256Mi limits — right-sized for kind clusters.
- Control Center set to 2 replicas.
Bug Fixes — CRD Health State Machine
CRD oscillating between healthy and pending on resync
LastReconcile()was callingh.pending.Store(true)as a side effect inside a getter. Every call toHealthAsMap()(on every health query) could silently flip a healthy CRD back to pending.SetStarted()was unconditionally settingpending=trueon every worker start, including resync-triggered restarts — overwriting thepending=falseset byRecordSuccess(). Now only sets pending if the CRD has not yet successfully reconciled.
CRD showing “not started” or “degraded” under network lag
postStartRetryIntervalwas left at 3 seconds (a debug value) instead of the intended 30 seconds. This caused the retry loop to hit the API server every 3 seconds across all CRDs continuously.crdExists()collapsed all errors — including network timeouts and dial failures — intofalse, treating any transient API server hiccup as “CRD disappeared.” Phase 1 (runtime disappearance check) and Phase 2 (missing CRD activation) would then callSetMissingAtRuntime()+SetDegraded(), flipping healthy CRDs to degraded and pending CRDs to “not started.”- Fixed:
crdExists()now returns a tri-state —(true, nil)exists,(false, nil)definitively absent,(false, err)transient. All three callers skip state changes whenerr != nil. postStartRetryIntervalrestored to 90 seconds with exponential backoff capped at 5 minutes.
RBAC — Namespace-Scoped ClusterRole Names
ClusterRoleandClusterRoleBindinggenerated byork generate bundleare now namedorkestra-<namespace>andorkestra-gateway-<namespace>. Previously all runtimes shared the same names, so the lastkubectl applywon — only that runtime’s RBAC rules survived.
Build — Docker Isolation
make docker,make docker-cc,make docker-gatewaynow build Linux binaries to/tmp/ork-docker-build/instead of~/.orkestra/bin/. Docker builds no longer overwrite the developer’s localorkCLI with a runtime binary (which has nogenerateorvalidatecommands).
Numbers
Memory baseline updated from estimate to measured: 79 MB RSS for a live 10-CRD runtime (process_resident_memory_bytes from /metrics). README and website updated accordingly.
Deletion Protection with Per‑CRD Overrides
- Added cluster‑wide deletion protection for any resource (custom or built‑in) labelled
orkestra.io/deletion-protection=true. - New
ValidatingWebhookConfiguration(protect.resources.orkestra.orkspace.io) intercepts DELETE on all configured GVRs and denies if the label is present. - Webhook rules are automatically built from:
- All custom CRDs enabled in the Katalog (respecting per‑CRD
protectCRsflag). - All built‑in resources marked
OrkestraInternal(deployments, services, namespaces, RBAC, etc.).
- All custom CRDs enabled in the Katalog (respecting per‑CRD
- When
security.deletionProtection.enabledis true, the reconciler automatically adds the protection label to every resource it creates or updates. - Per‑CRD overrides allow fine‑grained control:
deletionProtection: protectCRD: false # allow deletion of the CRD definition itself protectCRs: true # protect instances (only effective if protectCRD=true) strictMode: true # block removal of the protection label (per‑CRD override of global strictMode) - Validation warnings (
ork validate) appear for inconsistent combinations (e.g.,protectCRD=falsewithprotectCRs=true), guiding users toward correct configuration. - The
ork validateoutput now shows protection status with icons (🛡️ full, 🔓 CRD only, ⚠️ warning, ⛔ none) and lists warnings per CRD. - Fixed a bug where
customResourceGVRs()only added the first custom CRD’s GVR, causing incomplete webhook rules. - Added
cleanupOnShutdown: trueto automatically remove the webhook configuration when the Gateway stops gracefully.
Deletion Protection (Security)
- Added cluster‑wide deletion protection for any resource (custom or built‑in) labelled
orkestra.io/deletion-protection=true. - A new
ValidatingWebhookConfiguration(protect.resources.orkestra.orkspace.io) intercepts DELETE requests on all configured GVRs and denies them if the target has the protection label. - The webhook rules are automatically built from:
- All custom CRDs enabled in the Katalog.
- All built‑in resources marked
OrkestraInternal(e.g., deployments, services, namespaces, RBAC objects, etc.).
- When
security.deletionProtection.enabledis true, the reconciler automatically adds the protection label to every resource it creates or updates. - In standalone mode (no reconciler), users can manually label existing resources; the webhook still honours the label.
- Fixed a bug in
customResourceGVRs()that previously added only the first custom CRD GVR, causing incomplete webhook rules. - Webhook
failurePolicy: Failensures deletions are blocked when Orkestra Gateway is unreachable. cleanupOnShutdown: trueremoves the webhook configuration when the Gateway stops gracefully.
Example Katalog snippet:
security:
deletionProtection:
enabled: true # global switch
cleanupOnShutdown: true
failurePolicy: Fail
Usage:
- Orkestra‑managed resources are protected automatically.
- For external resources:
kubectl label <resource> orkestra.io/deletion-protection=true - Protected deletions are denied with a clear error message and remediation hint.
CHANGELOG — simulate, validate, e2e, ork-action, CRD-driven inference
Added — ork simulate (in-memory operator simulation)
New command that runs the full operator reconcile loop against a fake in-memory cluster — no Kubernetes required. Give it a Katalog and a CR and it shows exactly which resources are created, updated, or deleted each cycle.
pkg/simulate— new package:FakeKubeclient,Run,Result,CycleResult,Op- Fake clientset uses
PrependReactorso every k8s operation is recorded before the default object tracker handles it - CR is pre-seeded with managed labels and annotations so the reconciler’s idempotency guards skip those patches every cycle
- Deployment status is advanced to
Availableafter cycle 1 to unblock state machines waiting onAvailableReplicas - Steady state detected when two consecutive cycles produce identical verb+resource sequences;
Result.SteadyAtrecords the cycle number --cycles Nalways runs exactly N cycles; identical consecutive cycles are collapsed in output as(cycles X–Y: identical)+shown for creates,~for updates,-for deletes; if a resource is both created and patched in one cycle (e.g.reconcile: true), only+is shown- Zerolog global logger is redirected to
io.Discardduring simulation so reconciler JSON logs don’t pollute output - CR YAML is converted via
sigsyaml.YAMLToJSONbefore unmarshalling to ensure numeric fields arefloat64(k8sDeepCopyJSONrequirement) - Progressive documentation in
pkg/simulate/docs/(output, steady state, limitations, internals)
Added — ork e2e command
New command for declarative end-to-end tests against a real kind cluster. Provisions the cluster, installs operator dependencies, applies the CRD and bundle, starts the Orkestra runtime, applies the CR, and polls expectations.
docs/reference/cli/e2e.md— full CLI reference page- Added to
docs/reference/cli/index.mdoperator commands table
Improved — ork validate output
- Header now reads “Validating Katalog…” or “Validating Komposer…” based on the detected document kind
- E2E and Motif validation now use the same colour style as Katalog validation:
HealthIcon,ColorGray,Bold,ColorReset - Errors use
ColorRedconsistently across all document kinds
Added — CRD-driven API inference in ork run (dev mode)
crdFile:in Katalog spec populatesapiTypes(group, version, kind, plural) directly from the CRD YAML — no need to declareapiTypes:separately- Dev mode auto-applies CRDs before starting the runtime
- Dev mode auto-provisions a kind cluster when no cluster is available
Improved — ork-action (GitHub Action)
Replaced the complex Docker-based action with a minimal composite wrapper:
- Installs
orkvia curl, runsork e2e— no Dockerfile, no entrypoint script - Inputs:
e2e-file,ork-version,keep-cluster,cluster
CHANGELOG — onCreate.custom / onReconcile.custom: Operator Composition via Custom Resources
Added — Custom Resource lifecycle hooks (onCreate.custom / onReconcile.custom)
Introduced first-class support for composing operators by creating and managing arbitrary Kubernetes Custom Resources from within Orkestra hook declarations. An operator can now declare a custom block under onCreate or onReconcile to create, update, and conditionally clean up any CRD-backed resource — enabling true operator-to-operator composition without bespoke integrations.
Key components:
New types (
pkg/types/custom_resource.go) AddedCustomResourceTemplateSource,CustomResourceMetadata, andCustomResourceCondition.HasStatus *boolcontrols whether child status is read back into the parent resolver context.BuildGVK()andResolveGVR()methods provide GVK/GVR resolution from the declarative spec.Custom resource registry package (
pkg/resources/customresources/) New package exposingCreate,Update,DeleteIfOwned,Resolve, andResolvedCustomResourceSpec. Owner references are set automatically — deleting the parent cascades to all child custom resources without requiring anyonDeletehook.Template resolution (
pkg/resources/template/resolve_customresources.go) AddedResolveCustomResourceTemplateon the Resolver to expand motif templates and inject resolved values into the custom resource spec before apply.Katalog validation (
pkg/katalog/validate_custom_resource.go) Validation rules for custom resource declarations are enforced at startup in the Katalog layer, matching the validation model used for deployments, statefulsets, and jobs.Reconciler — run (
pkg/reconciler/run_customresource.go)runCustomResourcesevaluates conditions, checks GVK existence at runtime, and applies or cleans up child resources. If the target CRD is not installed at startup,runCustomResourcesskips gracefully and logs a warning; the kordinator’sretryMissingCRDsloop refreshes the REST mapper when the CRD later appears.Reconciler — forEach fan-out (
pkg/reconciler/expand_customresources.go)forEachsupport for custom resources mirrors the fan-out behaviour already available for deployments and statefulsets.Reconciler — child status (
pkg/reconciler/children.go)readCustomResourceGroupreads child CR status into the parent resolver context.hasStatus: falseskips the API call entirely (useful for fire-and-forget resources);trueornil(auto-detect) reads status back.Drift correction
Updatealways corrects spec and metadata drift.spec.Reconcileno longer gates drift detection — it only controls whetherUpdateis called on every reconcile cycle or only ononCreate.hasStatuspointer semanticshasStatusis a*boolpointer:nil= auto-detect,true= read child status into parent resolver context,false= skip. Orkestra does NOT write status to child CRs — Layer 1 (Ready) is only written to the owner CRD by the generic reconciler.Unified replica parsing (
pkg/resources/common/parse.go) AddedParseReplicas(s string) int32to unify replica string-to-int32 parsing across deployments, statefulsets, replicasets, pods, jobs, and cronjobs.Motif input quoting fix (
pkg/motif/expander.go)renderInputsnow strips YAML quotes for inputs declared astype: integerortype: bool, preventing theInvalid value: "string"class of errors when Orkestra-rendered values are applied to strictly-typed CRD fields.
Impact
Custom resource hooks enable Orkestra operators to compose other operators declaratively. Any CRD-backed resource can be created, updated, and garbage-collected as a first-class child of an Orkestra-managed CR. Missing CRDs are handled gracefully at runtime, and owner-reference-based cascading deletion removes the need for explicit cleanup hooks.
CHANGELOG — ONCOP Integration (Orkestra Native Cross‑Operator Protocol)
Added — ONCOP v1 (Orkestra Native Cross‑Operator Protocol)
Introduced ONCOP as the unified, typed, cross‑operator observation protocol for Orkestra. ONCOP replaces ad‑hoc HTTP integrations and hard‑coded URLs with a declarative, URL‑inferable, cache‑aware protocol used across autoscaling, status fields, and template resolution.
Key components:
Typed observation surfaces
Added first‑class ONCOP types:metrics,health,cr,info,events
Each type maps to a deterministic URL shape under/katalog/<crd>.URL inference engine
ImplementedBuildONCOPURLto construct ONCOP URLs fromCrossCRDDeclarationusing:source.host,source.type,crd,selector.namespace,selector.name.Cross‑operator resolver integration
UpdatedreadCross()to support ONCOP host‑based reads as Path 2, after informer registry and before raw endpoint fallback.
Responses injected into.cross.<as>for templates, autoscale conditions, and status fields.New ONCOP type:
cr
Addedtype: crfor CR‑specific detail (status,spec,children,metrics).
Distinguishes CR detail from CRD‑levelinfo.Autoscaler ONCOP support
Autoscale conditions now resolvecross.<crd>.metrics.*via ONCOP metrics endpoint with optional caching (cacheFor:).Resolver enhancements
AddedParseCrossFieldand extraction helpers (ExtractCrossCRD,ExtractCrossCategory,ExtractCrossFieldName,ExtractCrossNamespace) to unify cross‑field parsing.Fallback semantics
Resolution priority formalised as:informer registry → ONCOP host → raw endpoint → empty result.Cross‑binary caching
Added per‑source caching for ONCOP responses to avoid repeated remote calls.
Impact
ONCOP enables consistent, declarative, cross‑operator observation across Orkestra.
Autoscalers, status fields, and templates now consume cross‑operator data without bespoke integrations or hard‑coded URLs.
Operators implementing ONCOP become first‑class participants in the Orkestra ecosystem.