Skip to main content
root/enterprise-it-management/part-4-composition-functions-as-agentic-provisioning-gates

Part 4: Composition Functions as Agentic Provisioning Gates

How Crossplane Composition Functions enforce ledger-verified provisioning, blocking XR readiness until agent actions reconcile to MATCHED status.

Continue the series

You are reading Part 4 of 4

Building the Agentic Internal Developer Platform

View all parts
Latest published part
You are at the end of the published sequence.
Back to Part 3: Reconciling the Agentic Action Ledger
Part 4: Composition Functions as Agentic Provisioning Gates
13/07/2026|8 min read|

The reconciliation engine from Part 3 solves classification: every agent action lands in one of four buckets — MATCHED, ORPHAN_CREDENTIAL, ORPHAN_CLAIM, or DIVERGENT_OUTCOME. What it does not solve is enforcement at the point of provisioning. An agent can submit a Crossplane claim the instant it receives a credential from the broker, well before the reconciliation window closes. If that credential turns out to be orphaned or replayed, cloud resources may already exist. This part closes that gap using Crossplane Composition Functions as a synchronous policy gate inside the provisioning pipeline itself, rather than treating reconciliation as a purely after-the-fact audit trail.

#The Provisioning Race Condition

Crossplane’s control loop is optimistic by design: a Composite Resource (XR) transitions toward Ready: True as soon as its managed resources report healthy status. There is no native concept of “was this claim authored by a legitimately reconciled agent action.” Given the tolerance window (t_{window} = 5s) established in Part 3, an agent’s claim can reach the API server, get scheduled, and begin provisioning a VPC or IAM role well inside that window — before the ledger has confirmed a MATCHED state. This is the exact race the reconciliation engine was built to detect, but detection alone doesn’t stop the managed resource from being created.

The fix is to move enforcement upstream, into the composition pipeline, using Crossplane Composition Functions as the checkpoint. Rather than trusting the claim at admission time, the pipeline calls out to the reconciliation service on every reconcile pass and refuses to emit desired resources until the associated action is confirmed MATCHED.

#Architectural Breakdown: Gating with Crossplane Composition Functions

Since Crossplane v1.14, Compositions run as an ordered pipeline of gRPC functions (RunFunctionRequest / RunFunctionResponse), each receiving the observed and desired state and returning a mutated desired state plus status conditions. This pipeline model is what makes Crossplane Composition Functions suitable as an enforcement layer: a function can inspect the composite’s spec, call an external service synchronously, and either pass desired resources through unchanged or strip them entirely while setting Ready: False.

The gate function sits early in the pipeline, before function-patch-and-transform generates managed resources. If the gate fails, downstream functions never see resources to compose — the XR stalls in a Waiting condition rather than partially provisioning infrastructure. This is the same fail-closed posture used by the credential broker in Part 2, applied one layer up the stack. For teams standardising on this pattern across an internal provisioning architecture, the gate function becomes a reusable primitive rather than a one-off check.

#Implementation Logic

#Step 1: Carrying the Ledger Key on the Claim

Every claim submitted by an agent must carry the agentActionId issued at credential time, so the gate function has a key to query against the ledger.

Crossplane Composition Functions

1apiVersion: platform.kby.io/v1alpha1
2kind: AgenticClaim
3metadata:
4  name: agent-provision-vpc-7421
5spec:
6  agentActionId: "act_9f1c2e-7421"
7  parameters:
8    region: eu-west-2
9    cidrBlock: "10.40.0.0/16"

#Step 2: Wiring the Gate into the Composition Pipeline

The function-agentic-gate runs before patch-and-transform. Its input config carries the reconciliation service endpoint and the maximum wait budget before the XR is marked failed rather than pending.

1apiVersion: apiextensions.crossplane.io/v1
2kind: Composition
3metadata:
4  name: vpc-agentic.aws.platform.kby.io
5spec:
6  mode: Pipeline
7  pipeline:
8    - step: agentic-ledger-gate
9      functionRef:
10        name: function-agentic-gate
11      input:
12        apiVersion: gate.fn.kby.io/v1beta1
13        kind: GateConfig
14        reconciliationEndpoint: "reconciler.internal:9443"
15        requiredStatus: MATCHED
16        maxWaitSeconds: 8
17    - step: patch-and-transform
18      functionRef:
19        name: function-patch-and-transform
20    - step: auto-ready
21      functionRef:
22        name: function-auto-ready

#Step 3: The Gate Function Logic

Written against function-sdk-go, the function extracts agentActionId, queries the reconciliation service, and either forwards the request untouched or strips the desired composite entirely while attaching a condition.

1func (f *Function) RunFunction(ctx context.Context, req *fnv1.RunFunctionRequest) (*fnv1.RunFunctionResponse, error) {
2    rsp := response.To(req, response.DefaultTTL)
3    xr, _ := request.GetObservedCompositeResource(req)
4    actionID, _, _ := unstructured.NestedString(xr.Object, "spec", "agentActionId")
5
6    status, err := f.ledgerClient.Lookup(ctx, actionID)
7    if err != nil {
8        response.Fatal(rsp, errors.Wrap(err, "ledger unreachable"))
9        return rsp, nil
10    }
11
12    if status != "MATCHED" {
13        response.ConditionFalse(rsp, "Ready", "AwaitingReconciliation").
14            WithMessage(fmt.Sprintf("action %s status=%s", actionID, status))
15        response.SetDesiredCompositeResource(rsp, xr) // no managed resources attached
16        return rsp, nil
17    }
18
19    return rsp, nil // fall through, allow patch-and-transform to compose resources
20}

#Failure Modes and Edge Cases

The gate introduces new failure surfaces that did not exist in a pure audit-only reconciliation model:

Reconciliation service unavailability. If the ledger lookup times out, the function must fail closed via response.Fatal, forcing Crossplane to retry with backoff rather than defaulting to MATCHED. Fail-open here would defeat the entire purpose of the gate.

Post-gate divergence. A status of MATCHED at query time does not guarantee the action stays MATCHED. If the ledger later reclassifies the same action as DIVERGENT_OUTCOME — for instance because the downstream cloud provider call failed asynchronously — the XR has already progressed past the gate on that reconcile pass. Mitigation requires a secondary watch: the reconciliation service publishes reclassification events, and a controller subscribed to that stream can force-requeue the affected XR to re-enter the pipeline.

Replay via claim resubmission. An agent resubmitting the same agentActionId after a MATCHED result has already been consumed should be rejected at the ledger layer, not the gate. The gate function should treat a second lookup on an already-consumed action ID as ORPHAN_CLAIM, consistent with the classification rules from Part 3.

Crossplane Composition Functions

Deletion during pending gate. If the claim is deleted while the XR sits in Waiting, Crossplane’s finalizer logic must still allow the pipeline to run once more so the gate function can clean up any partial state — an easy oversight if the function assumes it only ever runs on create/update events.

#Scaling and Security Trade-offs

Introducing a synchronous external call into every Crossplane reconcile loop changes both the latency and the blast radius profile of the control plane:

  • Latency vs correctness: a synchronous gate adds (RTT_{ledger}) to every reconcile pass, typically 15–40ms under normal load, against near-zero enforcement latency in the ungated model. This is the direct cost of eliminating the provisioning race.
  • Fail-closed vs fail-open posture: fail-closed (used here) guarantees no orphaned infrastructure but risks stalling legitimate claims during a ledger outage; fail-open restores availability but reopens the exact race condition this design exists to close.
  • Fan-out at scale: thousands of XRs polling the reconciliation service on every reconcile interval creates read amplification against the ledger’s Postgres backend. A short-TTL cache (2–3s) in front of the lookup path absorbs repeated queries for the same agentActionId without weakening the tolerance window guarantees.
  • Blast radius of function compromise: the gate function holds a credential capable of querying the full ledger. It must run with a scoped read-only service account and mTLS to the reconciliation endpoint, never the broker-issuing credential from Part 2.
  • Operational observability: every AwaitingReconciliation condition should emit a metric labelled by agentActionId prefix and status, so a stuck queue of pending XRs is visible before it becomes a provisioning backlog.

Full technical detail on the pipeline execution model is documented in the official Crossplane Composition Functions documentation, which covers the gRPC contract this gate function implements against.

With enforcement now sitting inside the provisioning pipeline rather than bolted on as an audit step, the next part turns to what happens when the gate itself needs to reason about partial trust — agent actions that are MATCHED but scoped to resources the agent’s role should not touch.

Reader Interaction

Comments

Add a thoughtful note on Part 4: Composition Functions as Agentic Provisioning Gates. Comments are checked for spam and held for moderation before appearing.

Loading comments...

Loading anti-spam check...

Series navigation
Part 4 of 4 in Building the Agentic Internal Developer Platform
View full series
Eleanor Hayes

Written By

Eleanor Hayes

Dr Eleanor Hayes is a veteran cryptography researcher and enterprise security architect specialising in zero-trust network implementations. Having spent a decade securing critical national infrastructure, she now designs identity-aware proxy perimeters and mutual TLS topologies for highly distributed environments. She holds multiple GIAC certifications and regularly consults on mitigating advanced persistent threats within microservice ecosystems.