"Ask Kubernetes for a GitHub repository, a PostgreSQL database, or a full application deployment — get a real one back."
Note
W'xOps Core is a library of Crossplane v2 Configuration packages: each one defines a schema (an XRD) and the logic that
turns it into real infrastructure (a Composition), so kubectl apply -f my-app.yaml provisions a repository on Gitea or GitHub, a CloudNativePG database, or a
Deployment/Service/IngressRoute stack — no custom controller, no platform UI required to use it.
Standing up a new tenant app or database today usually means: click through Gitea's UI to create a repo, hand-write Terraform for the database, copy-paste a Deployment Service/Ingress from the last app that looked similar, and wire the secrets together by hand. Every step is a manual, undocumented, tribal-knowledge operation.
W'xOps Core turns each of those into a Kubernetes object with a schema: XScmRepository, XTenantDatabase, XTenantApp, and three more. Crossplane reconciles
them the same way it reconciles anything else — continuously, declaratively, with status fields you can poll instead of watching an apply scroll by. What
actually executes underneath (HCL run in-cluster against a Gitea or GitHub provider, or Kubernetes objects composed via KCL) is an implementation detail the schema hides.
This repo is only the Configuration packages — the schemas and the composition logic. It has no UI, no CLI, and no build pipeline; see Out of scope for the deliberate boundary.
Table of Contents
| Package | Kind | Group | API Versions | Last changed in |
|---|---|---|---|---|
platform-database-clusters |
XPlatformDatabaseCluster |
platform.wxops.cloud |
v1alpha1 |
release-2026-10-09 |
tenant-database |
XTenantDatabase |
platform.wxops.cloud |
v1alpha1 |
release-2026-10-09 |
tenant-app |
XTenantApp |
platform.wxops.cloud |
v1alpha1 |
release-2026-10-09 |
scm-connection |
XScmConnection |
scm.wxops.cloud |
v1alpha1 |
release-2026-10-09 |
scm-repository |
XScmRepository |
scm.wxops.cloud |
v1alpha1 |
release-2026-10-09 |
scm-oauth-app |
XScmOAuthApp |
scm.wxops.cloud |
v1alpha1 |
release-2026-10-09 |
Note
Served XRD API versions, and the release each package last changed in, are tracked in VERSIONS.yaml. Releases are named by date —
release-YYYY-MM-DD — and compatibility is the API version, not the release name; see development-docs/development/releasing.md.
Documentation has two homes, split by reader. docs/README.md is for using, operating and building on the platform
as it stands today. development-docs/README.md is for where it's headed and how it's built: the
development matrix tracking every package's and core idea's status, the core ideas themselves, and the decisions, proposals and roadmap.
| Section | For | Start with |
|---|---|---|
| API reference | What each Kind accepts, what Core composes and keeps reconciled, what it reports | The reconcile loop from your side, then one page per Kind |
| Core ideas | Darlane, Guardian, multi-cluster, observability, self-service operations, security | Solution matrix |
| User guide | Installing Core and building on it | Setup · App onboarding |
| Development | Changing, testing, releasing and rolling out Core, and the decisions behind it | Development guide · Releasing · RFC index |
The shape above is what every package in this repo is an instance of — a Configuration package contributing an XRD + Composition, which a
CompositeResourceDefinition turns into a composite resource (XR) that Crossplane creates and reconciles against real infrastructure. It's a structural map,
not a substitute for Crossplane's own docs — read those for what each piece actually does.
The previous direction used kubebuilder to build a controller from scratch. That was rejected — too much complexity for the problem.
The current approach combines two tools with clear roles:
- Crossplane owns the platform API layer:
XRDs define the schema,Compositions wire them to infrastructure, and the control loop reconciles desired state. - OpenTofu (via
provider-opentofu) owns the infrastructure execution: eachWorkspaceresource runs a plan/apply cycle in-cluster against a vendor's Terraform-compatible provider. OpenTofu is the open-source (MPL-2.0) fork of Terraform and reads the same HCL, which keeps the whole runtime stack open source — see ADR-003.provider-terraformis archived (providers/archive/), not installed bymake providers; the fourgitea-*packages that ran on it are themselves archived, not migrated — no cluster ever ran them (see RFC-003).
Composition functions may be written in Python, Go, CEL, KCL, or Go templating. The kcl/ directory holds KCL-based composition logic for
platform-database-clusters, tenant-database, and tenant-app — see KCL composition functions below.
package/ ← Crossplane Configuration packages, grouped by API group (RFC-007)
platform/
platform-database-clusters/
xrd.yaml ← XCompositeResourceDefinition (schema)
composition.yaml ← Composition (function-kcl)
crossplane.yaml ← Package descriptor for xpkg build (meta.pkg.crossplane.io/v1)
kustomization.yaml ← dev kustomize root (xrd + composition only)
README.md
tenant-database/
tenant-app/
archives/ ← retired, not deleted: gitea-user/-org/-team/-repository, random-password
scm/
connection/ ← XScmConnection — patch-and-transform + ESO ExternalSecret
repository/ ← XScmRepository — patch-and-transform + inline HCL
oauth-app/ ← XScmOAuthApp — patch-and-transform + inline HCL + ESO PushSecret
install/ ← Production install: OCI registry-based (pkg.crossplane.io/v1)
platform/
platform-database-clusters.yaml ← Configuration resource; spec.package pinned by `make release`
tenant-database.yaml
tenant-app.yaml
kustomization.yaml
kustomization.yaml ← kubectl apply -k package/install/
dev/
kustomization.yaml ← Development install: applies XRDs+Compositions directly, every group
← kubectl apply -k package/dev/
kustomization.yaml ← delegates to install/ (kubectl apply -k package/)
providers/ ← shared Provider + Function installs + ProviderConfig
archive/ ← provider-terraform, function-go-templating — kept, not installed
docs/ ← generic documentation hub (docs/README.md): using, operating, building on Core
learn/ ← Crossplane, OpenTofu and KCL as this repo uses them
api-reference/ ← the reconcile loop, one page per Kind, the status contract
_archives/ ← retired Kinds' reference docs
user-guide/ ← setup, app onboarding, portal integration
development-docs/ ← development hub (development-docs/README.md): matrix, decisions, process
core-ideas/ ← Darlane, Guardian, multi-cluster, observability, self-service, security
adr/ · rfc/ · development/ (guide, releasing) · _archives/ (ROADMAP.md, superseded by the RFC index)
examples/ ← minimal XR YAML to exercise each package
platform-database-clusters/xr.yaml
tenant-database/xr.yaml
tenant-database/xr-dedicated.yaml
tenant-app/xr.yaml
scm-connection/xr.yaml
scm-repository/xr.yaml
scm-oauth-app/xr.yaml
_archives/ ← examples for retired packages
kcl/ ← KCL composition functions (source of truth, embedded via kcl-sync)
platform-database-clusters/
kcl.mod
main.k
tenant-database/
kcl.mod
main.k
tenant-app/
kcl.mod
main.k
.github/workflows/
publish-packages.yaml ← CI: build + push changed packages on a release-* tag
pr-validate.yaml ← merge gate (mirrored in .gitea/workflows/ while PRs merge on Gitea)
tests/ ← offline suite: XRD conformance, API compat, golden, invariants
release-notes/ ← hand-written notes; required for careful/breaking releases
Note
To run these packages, you require to had the Kubernetes Cluster (>= v1.34.x), that quite be compatible with any distribution deployed, e.g: EKS, K3s, or Kind. If you want find the solution to self-hosted cluster (Kind, K3s, or RKE2), you can use project Kubewekend and follow the instruction.
For easier to test, you can use Kind as the local Kubernetes Cluster to run with only Docker, that will cost you few step to completely. Explore more at Kind - QuickStart
kind create cluster --name local --image kindest/node:v1.34.11After your cluster worked, you can install Kubectl, Helm and Bao CLI to continue work with cluster and easier install prerequisite tools for these package worked.
# Check your cluster actual worked with Kubernetes Components
kubectl get all -ANext, you will install OpenBao, ExternalSecret, and Crossplane Runtime with helm
# Helm apply for deploy OpenBao
helm repo add openbao https://openbao.github.io/openbao-helm
helm repo update
helm upgrade --install -n openbao openbao openbao/openbao --create-namespace --version 0.30.2 \
--set server.dev.enabled=true \
--set server.dev.devRootToken="root" \
--set injector.enabled=false
# Helm apply for deploy ESO (Extenal Secrets Operator)
helm repo add external-secrets https://charts.external-secrets.io
helm repo update
helm install -n external-secrets external-secrets external-secrets/external-secrets --create-namespace --version 1.2.1 \
--set installCRDs=true
# Helm apply for Crossplane Runtime
helm repo add crossplane https://charts.crossplane.io/stable
helm repo update
helm install -n crossplane-system crossplane crossplane/crossplane --create-namespace --version 2.3.1After these prerequisite components worked, you need to bootstrap the openbao for Kubernetes Authentication and make this one have worked with
external-secrets
# Port-forward OpenBao for easier worked (Open new terminal)
kubectl port-forward -n openbao services/openbao 8200:8200
# Export BAO_ADDR and BAO_TOKEN
export BAO_ADDR=http://127.0.0.1:8200
export BAO_TOKEN=root
# Enable the kubernetes auth method
bao auth enable -path kubernetes/ kubernetes
# update the kubernetes configuration
bao write auth/kubernetes/config \
kubernetes_host="https://kubernetes.default.svc"
# enable kv method and put the scm for connection
bao secrets enable -path platform/ -version=2 kv
# create role for secretStore for Kubernetes auth method
bao policy write eso-platform-rw - <<EOF
path "platform/data/*" {
capabilities = ["read", "list", "create", "update", "delete"]
}
path "platform/metadata/*" {
capabilities = ["read", "list", "create", "update", "delete"]
}
EOF
bao write auth/kubernetes/role/eso-platform-rw \
bound_service_account_names=external-secrets \
bound_service_account_namespaces=external-secrets \
policies=eso-platform-rw \
ttl=1m
# create secretStore for scm collected
cat <<EOF | kubectl apply -f -
apiVersion: external-secrets.io/v1
kind: ClusterSecretStore
metadata:
name: vault-platform
spec:
provider:
vault:
server: "http://openbao.openbao.svc.cluster.local:8200"
# The path within Vault where the secrets are stored. This is used as a prefix for all secret lookups.
path: "platform"
# Version is the Vault KV secret engine version.
# This can be either "v1" or "v2", defaults to "v2"
version: "v2"
# The authentication method to authenticate to Vault, e.g: AppRole, Kubernetes, ...
auth:
kubernetes:
mountPath: "kubernetes"
role: "eso-platform-rw"
EOF
# check the status of ClusterSecretStore worked or not (status valid, mean it worked)
kubectl get clustersecretstores.external-secrets.io vault-platformNext setup CrossPlane Providers, Functions, and Configuration required to operate these packages each others
make providers # providers once per cluster
make provider-configs # ProviderConfigs, once their CRDs are served
bao kv put platform/scm/github-wxops-idp/credentials token="your-admin-or-pat-token"
make install # packages from the OCI registry
kubectl api-resources | grep -i "wxops.cloud" # check the packages worked in your cluster as CRDs
kubectl apply -f examples/scm-connection/xr.yaml
kubectl apply -f examples/scm-repository/xr.yaml
kubectl get xscmrepositoriesPrerequisites, the platform dependencies each package needs, credential formats and uninstalling are in the setup guide. What each resource does once it exists is in the API reference.
pre-commit install --install-hooks
pre-commit install --hook-type pre-push --hook-type commit-msg
make test-deps && make test # the offline merge gate — no cluster neededEvery hook, every make target and the rules that bite are in the development guide; the change loop and the new-package checklist are in CONTRIBUTING.md.
platform-database-clusters, tenant-database, and tenant-app use function-kcl instead of
function-go-templating, since their Compositions need real branching/looping across multiple optional resources (conditional resource sets, dict merges, list
comprehensions over arrays like managedRoles[], and — for tenant-app — composing a nested XTenantDatabase XR). The scm-* packages are simple enough
that inline HCL via function-patch-and-transform is sufficient — no KCL, no vendor logic in the composition.
kcl/{pkg}/main.k is the source of truth and is embedded into package/platform/{pkg}/composition.yaml via make kcl-sync / make kcl-check.
See kcl/README.md for why KCL vs Go templating, the sync workflow, how to wire a new KCL module into an XRD, and the deferred OCI-modules
migration plan.
Two axes, never mixed: the XRD API version (platform.wxops.cloud/v1alpha1) is the contract that dev XRs, prod XRs and the portal bind to, and it only ever
grows once released; the release (release-YYYY-MM-DD) is a dated snapshot of the packages that changed.
make release # gate → pin changed packages → CHANGELOG.md → commit + tag release-YYYY-MM-DD
git push origin main && git push origin release-YYYY-MM-DDThe API rule, release notes, what CI publishes and the changelog are in Releasing.
See CONTRIBUTING.md for setup, the change loop, and the checklist for adding a new package.
Every change is gated by an offline test suite — no cluster required:
make test-deps # once
make test # XRD conformance + API compat + golden render tests + invariantscrossplane composition render runs the real function images in Docker, so the unit under test is the composition itself. See
tests/README.md for what that covers and, importantly, what it does not.
This project has a Code of Conduct. Found a vulnerability? See SECURITY.md for how to report it privately.
Licensed under the Apache License, Version 2.0.
Third-party components composed by this project are listed in NOTICE, which redistributors must preserve under Section 4(d) of the License. The
W'xOps name and marks are not granted by the License — see Section 6.