Skip to content
alethicPublic

About

Reactive, namespace-scoped resource composition for Kubernetes, running as a ServiceAccount you choose.

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Weft

Reactive, namespace-scoped resource composition for Kubernetes — running as a ServiceAccount you choose.

CI Latest release License


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 Weave is namespaced and self-contained. There is no cluster-scoped definition to register, and no CRD generated per composition. Anyone with edit in 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 a Weave cannot create or read anything that ServiceAccount could not.

At a glance

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   13s

Create 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.

Features

  • 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 from Ready and Degraded, 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() and select(), no recursion, no clock, and step, size and read budgets. Exceeding one fails that Weave, 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.

Install

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-namespace

That 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.com

Every chart value is documented in charts/weft/README.md, including hardening options such as confining impersonation to a set of namespaces.

Quick start

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-42
kubectl 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")},
        })
EOF
kubectl -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.

Documentation

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

Contributing

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.

License

Apache License 2.0.

About

Reactive, namespace-scoped resource composition for Kubernetes, running as a ServiceAccount you choose.

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages