Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 2 additions & 5 deletions authbridge/demos/echo/k8s/agent.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
# Echo demo agent — operator-injected, kagenti-UI-discoverable.
#
# Required labels for this agent to show up in the kagenti UI:
# kagenti.io/type: agent — UI lists agents by this label
# kagenti.io/inject: enabled — operator's webhook injects authbridge
# kagenti.io/spire: disabled — demo doesn't depend on SPIRE identity
# protocol.kagenti.io/a2a: "" — declares A2A protocol for the chat flow
Expand Down Expand Up @@ -35,10 +34,9 @@ metadata:
labels:
app.kubernetes.io/name: echo-agent
app.kubernetes.io/part-of: echo-demo
# Operator's AgentCardReconciler reads these from the workload
# Operator's AgentCardReconciler reads this from the workload
# (not the Pod template) to know what protocol to fetch the card
# under and to confirm the workload is an agent.
kagenti.io/type: agent
protocol.kagenti.io/a2a: ""
spec:
replicas: 1
Expand All @@ -50,7 +48,6 @@ spec:
labels:
app.kubernetes.io/name: echo-agent
app.kubernetes.io/part-of: echo-demo
kagenti.io/type: agent
kagenti.io/inject: enabled
kagenti.io/spire: disabled
protocol.kagenti.io/a2a: ""
Expand Down Expand Up @@ -145,7 +142,7 @@ spec:

# We intentionally do NOT define an AgentCard CR here — the operator
# auto-creates `<deployment>-deployment-card` for any Deployment with
# `kagenti.io/type=agent`. Defining our own collides with the
# the corresponding AgentRuntime CR. Defining our own collides with the
# admission webhook ("an AgentCard already targets Deployment X").
# The operator's CR points at the same Deployment, fetches our
# `/.well-known/agent-card.json` endpoint, and stuffs the result
Expand Down
7 changes: 2 additions & 5 deletions authbridge/demos/finance-sparc/k8s/agent.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
# finance-sparc demo agent — operator-injected, kagenti-UI-discoverable.
#
# Required labels (same contract as demos/ibac/k8s/agent.yaml):
# kagenti.io/type: agent — UI lists agents by this label
# kagenti.io/inject: enabled — operator's webhook injects authbridge
# kagenti.io/spire: disabled — demo doesn't depend on SPIRE identity
# protocol.kagenti.io/a2a: "" — declares A2A for the chat flow
Expand All @@ -25,7 +24,6 @@ metadata:
labels:
app.kubernetes.io/name: finance-agent
app.kubernetes.io/part-of: finance-sparc-demo
kagenti.io/type: agent
protocol.kagenti.io/a2a: ""
spec:
replicas: 1
Expand All @@ -37,7 +35,6 @@ spec:
labels:
app.kubernetes.io/name: finance-agent
app.kubernetes.io/part-of: finance-sparc-demo
kagenti.io/type: agent
kagenti.io/inject: enabled
kagenti.io/spire: disabled
protocol.kagenti.io/a2a: ""
Expand Down Expand Up @@ -106,8 +103,8 @@ spec:
type: ClusterIP
---
# AgentRuntime triggers operator-managed Keycloak client registration
# (the Secret the authbridge sidecar mounts at /shared/). The operator
# auto-creates the AgentCard CR for the kagenti.io/type=agent Deployment.
# (the Secret the authbridge sidecar mounts at /shared/) and auto-creation
# of an AgentCard CR for the agent deployment.
apiVersion: agent.kagenti.dev/v1alpha1
kind: AgentRuntime
metadata:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,13 @@
# - Calls the GitHub MCP tool via HTTP
# - Token exchange to the tool is handled transparently by the AuthBridge sidecar
#
# Labels (must be on Pod template):
# kagenti.io/type: agent - Required: identifies this as an agent workload
# Labels (on Pod template):
# kagenti.io/inject: enabled - Enables AuthBridge sidecar injection
# kagenti.io/spire: enabled - Enables SPIRE-based identity (set to "disabled" if no SPIRE)
#
# kagenti.io/type is applied automatically by the operator when the
# AgentRuntime CR (at the end of this file) is created.
#
# Usage:
# kubectl apply -f git-issue-agent-deployment.yaml

Expand Down Expand Up @@ -53,7 +55,6 @@ spec:
metadata:
labels:
app.kubernetes.io/name: git-issue-agent
kagenti.io/type: agent
kagenti.io/inject: enabled
kagenti.io/spire: enabled
spec:
Expand Down Expand Up @@ -140,3 +141,21 @@ spec:
targetPort: 8000
protocol: TCP
type: ClusterIP

---
# AgentRuntime triggers operator-managed Keycloak client registration
# (creates kagenti-keycloak-client-credentials-<hash> Secret with
# client-id.txt and client-secret.txt that the authbridge sidecar
# mounts at /shared/). The operator also applies kagenti.io/type=agent
# to the Deployment and Pod template, enabling webhook sidecar injection.
apiVersion: agent.kagenti.dev/v1alpha1
kind: AgentRuntime
metadata:
name: git-issue-agent
namespace: team1
spec:
type: agent
targetRef:
apiVersion: apps/v1
kind: Deployment
name: git-issue-agent
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# GitHub Tool (MCP Server) Deployment
#
# This is the GitHub MCP tool that provides GitHub API access.
# It does NOT have AuthBridge injection — the kagenti.io/type label is omitted
# so the webhook pre-filter skips it entirely.
# It does NOT have AuthBridge injection — there is no AgentRuntime CR
# for this workload, so the operator does not enroll it.
#
# The tool validates incoming tokens to determine authorization level:
# - Tokens with "github-full-access" scope → uses PRIVILEGED_ACCESS_PAT
Expand Down
7 changes: 2 additions & 5 deletions authbridge/demos/ibac/k8s/agent.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
# IBAC demo email-assistant agent — operator-injected, kagenti-UI-discoverable.
#
# Required labels for this agent to show up in the kagenti UI:
# kagenti.io/type: agent — UI lists agents by this label
# kagenti.io/inject: enabled — operator's webhook injects authbridge
# kagenti.io/spire: disabled — demo doesn't depend on SPIRE identity
# protocol.kagenti.io/a2a: "" — declares A2A protocol for the chat flow
Expand Down Expand Up @@ -33,10 +32,9 @@ metadata:
labels:
app.kubernetes.io/name: email-agent
app.kubernetes.io/part-of: ibac-demo
# Operator's AgentCardReconciler reads these from the workload
# Operator's AgentCardReconciler reads this from the workload
# (not the Pod template) to know what protocol to fetch the card
# under and to confirm the workload is an agent.
kagenti.io/type: agent
protocol.kagenti.io/a2a: ""
spec:
replicas: 1
Expand All @@ -48,7 +46,6 @@ spec:
labels:
app.kubernetes.io/name: email-agent
app.kubernetes.io/part-of: ibac-demo
kagenti.io/type: agent
kagenti.io/inject: enabled
kagenti.io/spire: disabled
protocol.kagenti.io/a2a: ""
Expand Down Expand Up @@ -147,7 +144,7 @@ spec:

# We intentionally do NOT define an AgentCard CR here — the operator
# auto-creates `<deployment>-deployment-card` for any Deployment with
# `kagenti.io/type=agent`. Defining our own collides with the
# the corresponding AgentRuntime CR. Defining our own collides with the
# admission webhook ("an AgentCard already targets Deployment X").
# The operator's CR points at the same Deployment, fetches our
# `/.well-known/agent-card.json` endpoint, and stuffs the result
Expand Down
19 changes: 19 additions & 0 deletions authbridge/demos/mtls/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,28 @@ CALLEE_ENVOY ?= mtls-callee-envoy
help:
@awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {printf " \033[36m%-32s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST)

.PHONY: wait-operator-labels
wait-operator-labels: ## Wait for the operator to apply kagenti.io/type from AgentRuntime CRs
@for deploy in $(CALLER) $(CALLEE); do \
echo "[*] Waiting for operator to label $$deploy ..."; \
for i in $$(seq 1 60); do \
val=$$(kubectl -n $(NAMESPACE) get deploy/$$deploy \
-o jsonpath="{.spec.template.metadata.labels['kagenti.io/type']}" 2>/dev/null); \
if [ "$$val" = "agent" ]; then echo " ✓ $$deploy labeled"; break; fi; \
sleep 2; \
done; \
val=$$(kubectl -n $(NAMESPACE) get deploy/$$deploy \
-o jsonpath="{.spec.template.metadata.labels['kagenti.io/type']}" 2>/dev/null); \
if [ "$$val" != "agent" ]; then \
echo "ERROR: operator did not label $$deploy within 120s"; exit 1; \
fi; \
done

.PHONY: deploy
deploy: ## Deploy caller + callee pods (both with mtls.mode: strict)
kubectl apply -f k8s/caller.yaml
kubectl apply -f k8s/callee.yaml
$(MAKE) wait-operator-labels
./scripts/patch-mtls-config.sh $(NAMESPACE) strict
kubectl -n $(NAMESPACE) rollout restart deploy/$(CALLER) deploy/$(CALLEE)
kubectl -n $(NAMESPACE) rollout status deploy/$(CALLER) deploy/$(CALLEE) --timeout=120s
Expand All @@ -26,6 +44,7 @@ demo-mtls: deploy ## Deploy + run the encryption-on-the-wire check (strict mode)
demo-mtls-permissive: ## Same shape but with mtls.mode: permissive; verify mixed-mode serving
kubectl apply -f k8s/caller.yaml
kubectl apply -f k8s/callee.yaml
$(MAKE) wait-operator-labels
./scripts/patch-mtls-config.sh $(NAMESPACE) permissive
kubectl -n $(NAMESPACE) rollout restart deploy/$(CALLER) deploy/$(CALLEE)
kubectl -n $(NAMESPACE) rollout status deploy/$(CALLER) deploy/$(CALLEE) --timeout=120s
Expand Down
9 changes: 5 additions & 4 deletions authbridge/demos/mtls/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -251,11 +251,12 @@ cleanly for envoy-sidecar too. The namespace-CM patch this demo uses
is a lighter-weight knob that avoids requiring a CR for every demo
pod.

### Demo pod manifests need `kagenti.io/type: agent`
### Demo pod enrollment via AgentRuntime

The kagenti-operator's mutating-webhook `objectSelector` requires
`kagenti.io/type` in `[agent, tool]`. The demo Pod templates now set
this label and an explicit `runAsUser: 100` on the `demo-app`
container (the `curlimages/curl:latest` image runs as the
`kagenti.io/type` in `[agent, tool]`. The demo manifests include
AgentRuntime CRs that enroll each workload — the operator applies
the label automatically. The `demo-app` containers set an explicit
`runAsUser: 100` (the `curlimages/curl:latest` image runs as the
non-numeric `curl_user`, which trips the kubelet's `runAsNonRoot`
check without an explicit numeric UID).
19 changes: 16 additions & 3 deletions authbridge/demos/mtls/k8s/callee.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,6 @@ spec:
app.kubernetes.io/part-of: mtls-demo
kagenti.io/inject: enabled
kagenti.io/spire: enabled
# Required by the kagenti-operator's mutating-webhook
# objectSelector ({kagenti.io/type: [agent, tool]}).
kagenti.io/type: agent
spec:
serviceAccountName: mtls-callee
securityContext:
Expand Down Expand Up @@ -89,3 +86,19 @@ spec:
protocol: TCP
name: http
type: ClusterIP

---
# AgentRuntime enrolls this workload with the operator. The operator
# applies kagenti.io/type=agent to the Deployment and Pod template,
# which triggers webhook sidecar injection and Keycloak client registration.
apiVersion: agent.kagenti.dev/v1alpha1
kind: AgentRuntime
metadata:
name: mtls-callee
namespace: team1
spec:
type: agent
targetRef:
apiVersion: apps/v1
kind: Deployment
name: mtls-callee
19 changes: 16 additions & 3 deletions authbridge/demos/mtls/k8s/caller.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,6 @@ spec:
app.kubernetes.io/part-of: mtls-demo
kagenti.io/inject: enabled
kagenti.io/spire: enabled
# Required by the kagenti-operator's mutating-webhook
# objectSelector ({kagenti.io/type: [agent, tool]}).
kagenti.io/type: agent
spec:
serviceAccountName: mtls-caller
securityContext:
Expand Down Expand Up @@ -96,3 +93,19 @@ spec:
protocol: TCP
name: http
type: ClusterIP

---
# AgentRuntime enrolls this workload with the operator. The operator
# applies kagenti.io/type=agent to the Deployment and Pod template,
# which triggers webhook sidecar injection and Keycloak client registration.
apiVersion: agent.kagenti.dev/v1alpha1
kind: AgentRuntime
metadata:
name: mtls-caller
namespace: team1
spec:
type: agent
targetRef:
apiVersion: apps/v1
kind: Deployment
name: mtls-caller
14 changes: 8 additions & 6 deletions authbridge/demos/weather-agent/demo-ui-advanced.md
Original file line number Diff line number Diff line change
Expand Up @@ -183,11 +183,11 @@ python demos/weather-agent/setup_keycloak_weather_advanced.py -n team1

> **Expected catalog quirk.** The **Agent Catalog** shows **two** entries:
> `weather-service-advanced` *and* `weather-tool-advanced`. The **Tool
> Catalog** is empty. This is by design — the advanced demo labels the
> tool with `kagenti.io/type: agent` so AuthBridge gets injected on it
> (the `injectTools` feature gate is off by default; see the
> [kubectl appendix](#operator-gotchas)). Pick `weather-service-advanced`
> for chat.
> Catalog** is empty. This is by design — the tool's AgentRuntime CR
> uses `type: agent` so the operator applies `kagenti.io/type=agent`
Comment thread
rh-dnagornuks marked this conversation as resolved.
> and AuthBridge gets injected (the `injectTools` feature gate is off
> by default; see the [kubectl appendix](#operator-gotchas)). Pick
> `weather-service-advanced` for chat.

1. **Agent Catalog** → namespace `team1` → `weather-service-advanced` →
**View Details**. The agent card should render (proves the agent is up and
Expand Down Expand Up @@ -302,12 +302,14 @@ If you'd rather skip the UI entirely, the same demo runs via raw manifests.
python authbridge/demos/weather-agent/setup_keycloak_weather_advanced.py \
-n team1 --wait-tool-client &

# 2. Apply manifests (the deploy script applies AgentRuntime CRs too)
# 2. Apply manifests
kubectl apply -f authbridge/demos/weather-agent/k8s/configmaps-advanced.yaml
kubectl apply -f authbridge/demos/weather-agent/k8s/weather-tool-advanced.yaml
kubectl apply -f authbridge/demos/weather-agent/k8s/agentruntime-weather-tool-advanced.yaml
kubectl rollout status deploy/weather-tool-advanced -n team1 --timeout=300s

kubectl apply -f authbridge/demos/weather-agent/k8s/weather-service-advanced.yaml
kubectl apply -f authbridge/demos/weather-agent/k8s/agentruntime-weather-service-advanced.yaml
kubectl rollout status deploy/weather-service-advanced -n team1 --timeout=420s

# 3. Re-run Keycloak setup so the agent client gets the optional exchange scope
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ spec:
template:
metadata:
labels:
kagenti.io/type: agent
protocol.kagenti.io/a2a: ""
app.kubernetes.io/name: weather-service-advanced
kagenti.io/inject: enabled
Expand Down
14 changes: 7 additions & 7 deletions authbridge/demos/weather-agent/k8s/weather-tool-advanced.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@
# The MCP server listens on port 8000. AuthBridge ext_proc listens on 9090
# inside the pod, so there is no port collision (unlike the GitHub tool on 9090).
#
# The kagenti AuthBridge mutating webhook only injects sidecars for pod
# labels kagenti.io/type: agent (see kagenti-operator/internal/webhook/
# in the kagenti-operator repo for the matching rule). This workload is
# still the weather MCP tool by image and name; the pod is labeled as agent so
# client-registration runs and registers the tool SPIFFE in Keycloak.
# The kagenti-operator's AgentRuntime CR (agentruntime-weather-tool-advanced.yaml)
# enrolls this workload and triggers AuthBridge sidecar injection. The
# AgentRuntime uses type "agent" so the operator applies kagenti.io/type=agent
# to the pod (the injectTools feature gate is off by default). This workload
# is still the weather MCP tool by image and Deployment name; it is enrolled
# as type "agent" so client-registration runs and registers the tool's
# SPIFFE identity in Keycloak.
#
# Usage:
# kubectl apply -f weather-tool-advanced.yaml
Expand Down Expand Up @@ -46,8 +48,6 @@ spec:
template:
metadata:
labels:
# Required for AuthBridge + SPIRE injection (webhook does not match type=tool)
kagenti.io/type: agent
app.kubernetes.io/name: weather-tool-advanced
app.kubernetes.io/component: mcp-tool
protocol.kagenti.io/mcp: ""
Expand Down
Loading