Skip to content

agentic-world-lab/kagent-nemoclaw

Repository files navigation

KAgent + NemoClaw

Deploy AI agents in isolated Kubernetes sandboxes.

KAgent orchestrates AI agents; OpenShell provides network-isolated sandbox pods managed by the Agent Sandbox Controller; NemoClaw is the AgentHarness that wires them together via the OpenClaw backend. Optionally connects to a Telegram bot channel.

┌──────────────────────────────────────────────┐
│  kagent namespace                            │
│  ┌───────────────┐    ┌────────────────────┐ │
│  │ kagent-ctrl   │───▶│ AgentHarness       │ │
│  └───────────────┘    │ (nemoclaw)         │ │
│                       └────────┬───────────┘ │
└────────────────────────────────┼─────────────┘
                                 │ OpenClaw backend
┌────────────────────────────────▼─────────────┐
│  openshell namespace                         │
│  ┌────────────────┐   ┌────────────────────┐ │
│  │ openshell gw   │──▶│ sandbox pod        │ │
│  │ (statefulset)  │   │ (network-isolated) │ │
│  └────────────────┘   └────────────────────┘ │
└──────────────────────────────────────────────┘
Policy enforcement: Kyverno (privileged + capabilities)
Sandbox CRD:        agent-sandbox-controller

Demo

KAgent + NemoClaw demo


Prerequisites

  • kubectl configured against the target cluster
  • helm v3+
  • Kubernetes 1.25+ (tested on K3s 1.35 / Ubuntu 24.04)
  • OpenAI API key

Configuration

Variable Required Default Description
OPENAI_API_KEY OpenAI key injected into KAgent
TELEGRAM_BOT_TOKEN Bot token; enables Telegram channel
TELEGRAM_ALLOWED_USERS Comma-separated Telegram user IDs
KYVERNO_VERSION 3.4.4
KAGENT_VERSION 0.9.2
OPENSHELL_CHART_VERSION 0.1.0
AGENT_SANDBOX_VERSION v0.4.5
SANDBOX_IMAGE ghcr.io/kagent-dev/nemoclaw/sandbox-base:2026.5.4
HARNESS_NAME nemoclaw Name of the AgentHarness CR

Quick Install

export OPENAI_API_KEY=sk-...
# export TELEGRAM_BOT_TOKEN=1234:AAE...
# export TELEGRAM_ALLOWED_USERS=8672152974

chmod +x install.sh && ./install.sh

Manual Step-by-Step

The sections below reproduce every command from install.sh as copy-paste blocks. Use these if you prefer full control or want to run only specific steps.

Set these shell variables before running any step:

export KYVERNO_VERSION=3.4.4
export KAGENT_VERSION=0.9.2
export OPENSHELL_CHART_VERSION=0.1.0
export AGENT_SANDBOX_VERSION=v0.4.5
export SANDBOX_IMAGE=ghcr.io/kagent-dev/nemoclaw/sandbox-base:2026.5.4
export HARNESS_NAME=nemoclaw
export OPENSHELL_NS=openshell
export KAGENT_NS=kagent
export SANDBOX_SYS_NS=agent-sandbox-system

Step 0 — Kyverno + Runtime Policy

Install Kyverno:

helm upgrade --install kyverno \
  oci://ghcr.io/kyverno/charts/kyverno \
  --version "$KYVERNO_VERSION" \
  --namespace kyverno \
  --create-namespace \
  --wait --timeout=300s

Grant Kyverno read access to Sandbox CRs:

kubectl apply -f - <<'EOF'
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: kyverno-sandbox-reporter
rules:
  - apiGroups: ["agents.x-k8s.io"]
    resources: ["sandboxes"]
    verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: kyverno-sandbox-reporter
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: kyverno-sandbox-reporter
subjects:
  - kind: ServiceAccount
    name: kyverno-reports-controller
    namespace: kyverno
EOF

Apply the runtime policy that grants sandbox pods privileged access:

kubectl apply -f - <<'EOF'
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: openshell-runtime-access
spec:
  rules:
    - name: privileged-openshell
      match:
        any:
          - resources:
              kinds:
                - Sandbox
              namespaces:
                - openshell
              selector:
                matchLabels:
                  openshell.ai/managed-by: openshell
      mutate:
        foreach:
          - list: "request.object.spec.podTemplate.spec.containers"
            patchesJson6902: |-
              - op: add
                path: /spec/podTemplate/spec/containers/{{elementIndex}}/securityContext/privileged
                value: true

              - op: add
                path: /spec/podTemplate/spec/containers/{{elementIndex}}/securityContext/runAsUser
                value: 0

              - op: add
                path: /spec/podTemplate/spec/containers/{{elementIndex}}/securityContext/capabilities
                value:
                  add:
                    - SYS_ADMIN
                    - NET_ADMIN
                    - SYS_PTRACE
                    - SYSLOG
EOF

Step 1 — Agent Sandbox Controller

kubectl apply -f "https://github.com/kubernetes-sigs/agent-sandbox/releases/download/${AGENT_SANDBOX_VERSION}/manifest.yaml"
kubectl rollout status deployment/agent-sandbox-controller \
  -n "$SANDBOX_SYS_NS" --timeout=120s

Apply the RBAC fix for event recording:

kubectl apply -f - <<EOF
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: agent-sandbox-controller-events
  namespace: $SANDBOX_SYS_NS
rules:
- apiGroups: [""]
  resources: ["events"]
  verbs: ["create","patch","update"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: agent-sandbox-controller-events
  namespace: $SANDBOX_SYS_NS
subjects:
- kind: ServiceAccount
  name: agent-sandbox-controller
  namespace: $SANDBOX_SYS_NS
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: agent-sandbox-controller-events
EOF

Step 2 — OpenShell

Create namespace and SSH handshake secret:

kubectl create namespace "$OPENSHELL_NS" --dry-run=client -o yaml | kubectl apply -f -

kubectl create secret generic openshell-ssh-handshake \
  --from-literal=secret="$(openssl rand -hex 32)" \
  -n "$OPENSHELL_NS"

Install the OpenShell chart:

helm install openshell \
  oci://ghcr.io/kagent-dev/openshell/charts/openshell \
  --version "$OPENSHELL_CHART_VERSION" \
  --namespace "$OPENSHELL_NS" \
  --values - <<EOF
image:
  repository: ghcr.io/kagent-dev/openshell/gateway
  tag: latest
  pullPolicy: Always
supervisor:
  image:
    repository: ghcr.io/kagent-dev/openshell/supervisor
    tag: latest
server:
  sandboxNamespace: $OPENSHELL_NS
  sandboxImage: $SANDBOX_IMAGE
  disableTls: true
  disableGatewayAuth: false
sshHandshake:
  hook:
    enabled: false
EOF

kubectl rollout status statefulset/openshell -n "$OPENSHELL_NS" --timeout=120s

Step 3 — KAgent

Create the namespace and inject the OpenAI secret:

kubectl create namespace "$KAGENT_NS" --dry-run=client -o yaml | kubectl apply -f -

kubectl create secret generic kagent-openai \
  --from-literal=OPENAI_API_KEY="$OPENAI_API_KEY" \
  -n "$KAGENT_NS" --dry-run=client -o yaml | kubectl apply -f -

# Allow Helm to adopt the pre-existing secret
kubectl annotate secret kagent-openai -n "$KAGENT_NS" \
  meta.helm.sh/release-name=kagent \
  meta.helm.sh/release-namespace="$KAGENT_NS" \
  --overwrite

kubectl label secret kagent-openai -n "$KAGENT_NS" \
  app.kubernetes.io/managed-by=Helm \
  --overwrite

Install KAgent CRDs and controller:

helm upgrade --install kagent-crds \
  oci://ghcr.io/kagent-dev/kagent/helm/kagent-crds \
  --version "$KAGENT_VERSION" \
  --namespace "$KAGENT_NS"

helm upgrade --install kagent \
  oci://ghcr.io/kagent-dev/kagent/helm/kagent \
  --set controller.image.registry=fjvicens \
  --set controller.image.repository=kagent-ctrl \
  --set controller.image.tag=latest \
  --version "$KAGENT_VERSION" \
  --namespace "$KAGENT_NS" \
  --values - <<EOF
providers:
  default: openAI
  openAI:
    apiKey: "$OPENAI_API_KEY"
controller:
  env:
    - name: OPENSHELL_GATEWAY_URL
      value: "openshell.${OPENSHELL_NS}.svc.cluster.local:8080"
    - name: OPENSHELL_INSECURE
      value: "true"
EOF

kubectl rollout status deployment/kagent-controller -n "$KAGENT_NS" --timeout=120s

Step 4 — AgentHarness

Apply the NemoClaw AgentHarness:

kubectl apply -f - <<EOF
apiVersion: kagent.dev/v1alpha2
kind: AgentHarness
metadata:
  name: $HARNESS_NAME
  namespace: $KAGENT_NS
spec:
  backend: openclaw
  modelConfigRef: default-model-config
EOF

Telegram: To enable a Telegram channel, set TELEGRAM_BOT_TOKEN and TELEGRAM_ALLOWED_USERS and run ./install.sh instead — it builds the channels block automatically. See TROUBLESHOOTING.md if the Telegram proxy policy gets stuck in pending.

Wait for the sandbox to be provisioned:

# Poll until backendRef is populated (up to 90s)
deadline=$((SECONDS + 90))
until kubectl get agentharness "$HARNESS_NAME" -n "$KAGENT_NS" \
    -o jsonpath='{.status.backendRef.id}' 2>/dev/null | grep -q .; do
  (( SECONDS < deadline )) || { echo "Timeout waiting for sandbox"; exit 1; }
  sleep 3
done

echo "Sandbox: $(kubectl get agentharness "$HARNESS_NAME" -n "$KAGENT_NS" \
  -o jsonpath='{.status.backendRef.id}')"

Expose the KAgent UI:

kubectl patch svc kagent-ui -n kagent -p '{"spec":{"type":"LoadBalancer"}}'

Verification

kubectl get pods -n openshell
kubectl get pods -n kagent
kubectl get agentharness -n kagent
kubectl logs -n kagent deployment/kagent-controller --tail=30

Notes

  • The Kyverno openshell-runtime-access policy automatically patches new sandbox pods with privileged: true. If a sandbox pod was created before Kyverno was ready, delete it so the policy applies on re-creation.
  • Telegram channel requires TELEGRAM_BOT_TOKEN and TELEGRAM_ALLOWED_USERS to be set before the AgentHarness is created. The easiest path is to export them and run ./install.sh.
  • See TROUBLESHOOTING.md for known OpenClaw provisioning issues and their workarounds.

About

Installation of nemoclaw on kagent

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages