Reactive, namespace-scoped resource composition for Kubernetes — running as a ServiceAccount you choose.
Weft replaces the CronJob that runs kubectl get | template | kubectl apply.
You write one namespaced custom resource, a Weave. It reads resources that
already exist in its namespace, runs a small Starlark
program over them, and applies whatever the program declares. When anything it
read changes, it runs again. What it created is owned, kept current, pruned when
no longer declared, and torn down in order when the Weave is deleted.
Two properties set it apart:
- No platform team required. A
Weaveis namespaced and self-contained. There is no cluster-scoped definition to register, and no CRD generated per composition. Anyone witheditin a namespace can write one. - It can never exceed its author's permissions. Every read and every write
is impersonated as a ServiceAccount named in the
Weave. The controller holds no write access of its own to anything a program generates, so aWeavecannot create or read anything that ServiceAccount could not.
apiVersion: weft.alethic.solutions/v1
kind: Weave
metadata:
name: app
namespace: my-namespace
spec:
serviceAccountName: composer # every read and write happens as this identity
variables: # optional configuration, merged in order
- configMap:
name: platform # a shared base somebody else maintains
- values:
prefix: demo # overrides for this Weave
program: |
def compose(variable):
tenant = read("v1", "ConfigMap", "tenant")
if not tenant:
return wait("no tenant ConfigMap in this namespace yet")
resource({
"apiVersion": "v1",
"kind": "ConfigMap",
"metadata": {"name": variable.prefix + "-settings"},
"data": {"tenantId": require(tenant, "data.tenantId")},
})$ kubectl get weave
NAME READY WAITING AGE
app True Applied 13sCreate it and demo-settings appears. Edit the tenant ConfigMap and it
updates. Delete the Weave and it goes away.
If composer is not allowed to read tenant or write demo-settings, nothing
is created, and the Weave's status contains the exact kubectl commands that
would grant what is missing.
- Reactive. Watches are derived from what the program actually read, so there is nothing to declare and nothing to keep in step.
- Waits are first-class. A program can be not ready yet — blocked on a
resource that does not exist, or on a status field another controller has not
filled in. That is reported as
Waiting, distinct fromReadyandDegraded, and resolves itself when the field appears. - Staged rollout. A program can see what it previously created, read back live, and declare only what it can so far — the natural shape for infrastructure that provisions in steps.
- Safe ownership. Weft refuses to take over an object it did not create.
Handing one over takes an annotation on that object. Individual resources can
opt out of ownership so they outlive the
Weave. - Ordered teardown. Declaration order is apply order, and teardown runs in
reverse.
needs=lets independent siblings be deleted in parallel. - Cautious pruning. A resource that disappears from the program is deleted only after it has stayed gone for a configurable delay, so a provider restart does not churn real infrastructure.
- Bounded evaluation. Programs are untrusted by design: no I/O beyond
read()andselect(), no recursion, no clock, and step, size and read budgets. Exceeding one fails thatWeave, never the controller. - Provider-agnostic. Weft composes whatever CRDs are installed — Crossplane managed resources, cert-manager, your own — and knows nothing about any of them.
Requires Kubernetes 1.27 or later and Helm 3.8 or later.
helm install weft oci://ghcr.io/alethic/charts/weft \
--namespace weft-system --create-namespaceThat installs the latest release. Add --version with a version from the
releases page to pin one, which is
what you want anywhere you care about repeatability.
Each chart pins the controller image (ghcr.io/alethic/weft) by digest, so an
installed release cannot drift under a moved tag. The image and the chart are
both signed with cosign, keyless, and
each release lists the image digest. To verify an image before you run it:
cosign verify ghcr.io/alethic/weft@<digest> \
--certificate-identity-regexp '^https://github.com/alethic/weft/' \
--certificate-oidc-issuer https://token.actions.githubusercontent.comEvery chart value is documented in charts/weft/README.md, including hardening options such as confining impersonation to a set of namespaces.
With the controller installed, everything below runs as an ordinary namespace user — there is no administrator step.
kubectl create namespace weft-quickstart
kubectl -n weft-quickstart create serviceaccount composer
kubectl -n weft-quickstart create rolebinding composer-edit \
--clusterrole=edit --serviceaccount=weft-quickstart:composer
kubectl -n weft-quickstart create configmap tenant --from-literal=tenantId=acme-42kubectl apply -f - <<'EOF'
apiVersion: weft.alethic.solutions/v1
kind: Weave
metadata:
name: hello
namespace: weft-quickstart
spec:
serviceAccountName: composer
program: |
def compose(variable):
tenant = read("v1", "ConfigMap", "tenant")
if not tenant:
return wait("no tenant ConfigMap yet")
resource({
"apiVersion": "v1",
"kind": "ConfigMap",
"metadata": {"name": "settings"},
"data": {"tenantId": require(tenant, "data.tenantId")},
})
EOFkubectl -n weft-quickstart get weave,configmap
kubectl -n weft-quickstart patch configmap tenant -p '{"data":{"tenantId":"acme-43"}}'
kubectl -n weft-quickstart get configmap settings -o jsonpath='{.data.tenantId}'The edit binding is the quick path. In production, scope the ServiceAccount
to exactly what its program needs — docs/rbac.md explains how,
and a Weave that is missing a permission tells you which one.
To go further, demo/ walks through seven Weaves covering
staging, waiting, permission failures, ordered teardown, pruning, finalizers
and the evaluator's limits, using only core Kubernetes types.
examples/ has longer compositions taken from real Crossplane
workloads.
| The composition language | compose(), resource(), read(), select(), wait(), pending(), variables, and every built-in |
| Ownership | what Weft manages, adopting existing objects, opting out, renaming, deletion |
| Permissions | what a Weave's ServiceAccount needs, and what the controller itself holds |
| Operating Weft | status conditions, pruning, teardown, controller flags, metrics, uninstalling |
| Design and alternatives | how Weft compares with kro, Crossplane, Flux and Kyverno; why Starlark; non-goals |
| Helm chart | installation, every value, common configurations |
| Moving from weft.run | for installations that use the older weft.run API group |
| Security | threat model, hardening, reporting a vulnerability |
Issues and pull requests are welcome. CONTRIBUTING.md covers building, the test layers, and the two design properties a change must not weaken. Please report security issues privately as described in SECURITY.md.