Skip to content
Open
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
17 changes: 17 additions & 0 deletions examples/cross-namespace-a2a/00-namespaces.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Two namespaces on the same cluster. Labels drive the AllowedNamespaces
# selector on the specialist — only namespaces with team=alpha can reference it.
apiVersion: v1
kind: Namespace
metadata:
name: team-alpha
labels:
team: alpha
kagent.dev/agent-consumer: "true"
---
apiVersion: v1
kind: Namespace
metadata:
name: team-beta
labels:
team: beta
kagent.dev/agent-provider: "true"
75 changes: 75 additions & 0 deletions examples/cross-namespace-a2a/01-rbac.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# ServiceAccounts for each agent. Agents run with least-privilege:
# they can only read Secrets in their own namespace.
# The kagent controller itself has cluster-scoped Agent/ModelConfig read
# (granted by the helm chart) — these bindings are agent-runtime only.

# --- team-beta: specialist ---
apiVersion: v1
kind: ServiceAccount
metadata:
name: specialist-agent
namespace: team-beta
---
Comment on lines +7 to +12
# Specialist can read its own secrets (for model API key resolution)
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: specialist-agent
namespace: team-beta
rules:
- apiGroups: [""]
resources: ["secrets", "configmaps"]
verbs: ["get", "list"]
- apiGroups: ["kagent.dev"]
resources: ["agents", "modelconfigs"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: specialist-agent
namespace: team-beta
subjects:
- kind: ServiceAccount
name: specialist-agent
namespace: team-beta
roleRef:
kind: Role
name: specialist-agent
apiGroup: rbac.authorization.k8s.io

# --- team-alpha: orchestrator ---
apiVersion: v1
kind: ServiceAccount
metadata:
name: orchestrator-agent
namespace: team-alpha
---
# Orchestrator can read its own secrets. Critically, it CANNOT read
# team-beta secrets — cross-namespace Secret access is not granted.
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: orchestrator-agent
namespace: team-alpha
rules:
- apiGroups: [""]
resources: ["secrets", "configmaps"]
verbs: ["get", "list"]
- apiGroups: ["kagent.dev"]
resources: ["agents", "modelconfigs"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: orchestrator-agent
namespace: team-alpha
subjects:
- kind: ServiceAccount
name: orchestrator-agent
namespace: team-alpha
roleRef:
kind: Role
name: orchestrator-agent
apiGroup: rbac.authorization.k8s.io
128 changes: 128 additions & 0 deletions examples/cross-namespace-a2a/02-network-policy.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
# NetworkPolicies: default-deny then allow only the kagent controller
# to reach agent pods. Agents cannot call each other directly — all A2A
# traffic routes through the gateway (controller service on port 8083).

# --- team-beta: deny all ingress, allow only from kagent controller ---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: team-beta
spec:
podSelector: {}
policyTypes:
- Ingress
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-kagent-controller
namespace: team-beta
spec:
podSelector:
matchLabels:
app.kubernetes.io/name: specialist-agent
policyTypes:
Comment on lines +22 to +25
- Ingress
ingress:
# Only the kagent controller pod may initiate connections to the specialist.
# The controller lives in the kagent namespace and carries this label.
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kagent
podSelector:
matchLabels:
app.kubernetes.io/name: kagent
Comment on lines +34 to +36
ports:
- protocol: TCP
port: 8080 # agent HTTP port (A2A endpoint)

# --- team-alpha: deny all ingress, allow only from kagent controller ---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: team-alpha
spec:
podSelector: {}
policyTypes:
- Ingress
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-kagent-controller
namespace: team-alpha
spec:
podSelector:
matchLabels:
app.kubernetes.io/name: orchestrator-agent
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kagent
podSelector:
matchLabels:
app.kubernetes.io/name: kagent
ports:
- protocol: TCP
port: 8080

# --- Both namespaces: allow egress only to kagent controller + kube-dns ---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-egress-to-kagent
namespace: team-alpha
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kagent
ports:
- protocol: TCP
port: 8083 # kagent A2A gateway port
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
ports:
- protocol: UDP
port: 53 # kube-dns
- protocol: TCP
port: 53
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-egress-to-kagent
namespace: team-beta
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kagent
ports:
- protocol: TCP
port: 8083
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
Comment on lines +75 to +128
50 changes: 50 additions & 0 deletions examples/cross-namespace-a2a/03-secrets.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Each namespace holds only its own secrets. The orchestrator in team-alpha
# holds a delegation token it passes as X-Agent-Token when calling the
# specialist. The specialist validates this header (or will once OIDC lands).
#
# DO NOT commit real keys — replace these base64 values before applying.
# Generate: echo -n 'your-key' | base64

# --- team-beta: specialist model API key ---
apiVersion: v1
kind: Secret
metadata:
name: openai-secret
namespace: team-beta
type: Opaque
data:
# echo -n 'sk-YOUR-KEY-HERE' | base64
api-key: c2stWU9VUi1LRVktSEVSRQ==
---
# Token the specialist uses to validate callers. The orchestrator must
# present this in X-Agent-Token. Rotate independently of model keys.
apiVersion: v1
kind: Secret
metadata:
name: specialist-delegation-token
namespace: team-beta
type: Opaque
data:
# echo -n 'change-me-strong-random-token' | base64
token: Y2hhbmdlLW1lLXN0cm9uZy1yYW5kb20tdG9rZW4=

# --- team-alpha: orchestrator model API key ---
apiVersion: v1
kind: Secret
metadata:
name: openai-secret
namespace: team-alpha
type: Opaque
data:
api-key: c2stWU9VUi1LRVktSEVSRQ==
---
# Delegation token the orchestrator presents to the specialist.
# Must match specialist-delegation-token.token in team-beta.
apiVersion: v1
kind: Secret
metadata:
name: specialist-delegation-token
namespace: team-alpha
type: Opaque
data:
token: Y2hhbmdlLW1lLXN0cm9uZy1yYW5kb20tdG9rZW4=
24 changes: 24 additions & 0 deletions examples/cross-namespace-a2a/04-model-configs.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# ModelConfig per namespace — each team owns its own model config and API key.
# Neither can read the other's Secret.

apiVersion: kagent.dev/v1alpha2
kind: ModelConfig
metadata:
name: default-model
namespace: team-beta
spec:
provider: OpenAI
model: gpt-4o-mini
apiKeySecret: openai-secret
apiKeySecretKey: api-key
---
apiVersion: kagent.dev/v1alpha2
kind: ModelConfig
metadata:
name: default-model
namespace: team-alpha
spec:
provider: OpenAI
model: gpt-4o-mini
apiKeySecret: openai-secret
apiKeySecretKey: api-key
40 changes: 40 additions & 0 deletions examples/cross-namespace-a2a/05-specialist-agent.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Specialist agent in team-beta.
#
# Key security field: spec.allowedNamespaces
# The reconciler calls AllowsNamespace() before wiring this agent as a tool.
# Any Agent in a namespace NOT matching the selector will be rejected at
# reconcile time — the orchestrator's tool reference will fail with an error
# and the orchestrator agent will not be admitted.
#
# Pattern follows Gateway API cross-namespace route attachment:
# https://gateway-api.sigs.k8s.io/guides/multiple-ns/#cross-namespace-routing

apiVersion: kagent.dev/v1alpha2
kind: Agent
metadata:
name: specialist
namespace: team-beta
spec:
type: Declarative
declarative:
description: >
Math specialist. Solves arithmetic, algebra, and calculus problems
step by step. Only accepts delegations from team-alpha.
Comment on lines +18 to +22
systemMessage: |
You are a precise math specialist. When given a problem:
1. State the problem clearly.
2. Show every step.
3. State the final answer on its own line prefixed with "Answer:".
Never guess. If unsure, say so.
modelConfig: default-model
tools: []

# Cross-namespace access control — Gateway API pattern.
# Only namespaces labelled team=alpha may reference this agent as a tool.
# Change to `from: All` to allow any namespace (not recommended for prod).
# Change to `from: Same` (or omit) to lock to team-beta only.
allowedNamespaces:
from: Selector
selector:
matchLabels:
team: alpha
50 changes: 50 additions & 0 deletions examples/cross-namespace-a2a/06-orchestrator-agent.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Orchestrator agent in team-alpha.
#
# Calls the specialist in team-beta as a tool via the kagent A2A gateway.
# The gateway URL pattern is:
# <controller-svc>:8083/api/a2a/team-beta/specialist/
#
# headersFrom injects the delegation token from team-alpha's own Secret as
# X-Agent-Token on every outbound A2A call to the specialist. The specialist
# can validate this header. Crucially, the orchestrator never touches
# team-beta's Secrets — it only presents a matching token from its own namespace.
#
# If the specialist's allowedNamespaces selector does NOT match team-alpha,
# the controller reconciler rejects this Agent at admission and logs:
# "cross-namespace reference denied: team-alpha -> team-beta/specialist"

Comment on lines +12 to +15
apiVersion: kagent.dev/v1alpha2
kind: Agent
metadata:
name: orchestrator
namespace: team-alpha
spec:
type: Declarative
declarative:
description: >
Orchestrator agent. Receives user requests and delegates math problems
to the team-beta specialist via the kagent Agent Gateway.
systemMessage: |
You are an orchestrating agent. For any math question, delegate it to
the specialist tool and return the specialist's answer verbatim.
Do not attempt to solve math yourself.
modelConfig: default-model
tools:
# Cross-namespace agent tool reference.
# `name` is the Agent name; `namespace` resolves the cross-namespace ref.
# The reconciler validates team-alpha is allowed by team-beta/specialist
# before this agent is admitted.
- agent:
name: specialist
namespace: team-beta
# headersFrom: inject delegation token from THIS namespace's Secret.
# The controller translator resolves the Secret at reconcile time and
# adds X-Agent-Token to every A2A request to the specialist.
# The specialist's auth layer (UnsecureAuthenticator today, JWT once
# EP-476 lands) can validate this header.
headersFrom:
- name: X-Agent-Token
valueFrom:
type: Secret
name: specialist-delegation-token
key: token
Loading
Loading