From 33f102099742c2aa7186af3d5b90220589aa0ab2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20Schmitz=20von=20H=C3=BClst?= Date: Wed, 21 Oct 2026 08:34:53 +0200 Subject: [PATCH 1/2] docs: cover namespace-restricted installs on the 1.x installation page (#629) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Moritz Schmitz von Hülst --- .../content/kagent/1.x/setup/installation.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/docs-site/content/kagent/1.x/setup/installation.md b/docs-site/content/kagent/1.x/setup/installation.md index 9c2226a7..15765285 100644 --- a/docs-site/content/kagent/1.x/setup/installation.md +++ b/docs-site/content/kagent/1.x/setup/installation.md @@ -146,6 +146,25 @@ The kagent CLI installs the CRDs, Agent Substrate, the Agent Substrate PodCertif To serve the UI behind a reverse proxy under a prefix such as `/ui` (configurable with `ui.basePath`), see [Serve the UI under a sub-path]({{< link path="setup/reverse-proxy" >}}). +## Restrict kagent to specific namespaces + +By default the controller watches every namespace, and the chart creates cluster-scoped RBAC (ClusterRoles and ClusterRoleBindings). To restrict kagent to a set of namespaces, set `global.watchNamespaces` in your Helm values: + +```yaml +global: + watchNamespaces: [prod-agents, staging-agents] +``` + +Prefer `global.watchNamespaces` over the per-surface keys. The chart derives both surfaces from this one value, so they cannot disagree: it replaces the cluster-scoped getter and writer ClusterRoles with a Role and RoleBinding in each listed namespace, and it limits the controller's watch to the same list. The chart appends the install namespace to the list automatically, so the release's own objects remain managed. + +The per-surface keys still exist for installs that scope the surfaces independently, and the validation rules bind them: + +- `rbac.namespaces` takes precedence over `global.watchNamespaces` whenever the key is present. An explicit `rbac.namespaces: []` restores cluster-scoped RBAC. When `rbac.namespaces` is set, it must include the install namespace or the render fails. +- `config.kubernetes.watchNamespaces` narrows the watch below the RBAC scope. A watch entry that names a namespace outside the RBAC scope fails the render, because the controller would loop on `Forbidden` errors at runtime. +- The `kagent-tool-server` RemoteMCPServer that the chart creates admits references from its own namespace only. An Agent in another watched namespace that binds it does not become ready until you set `allowedNamespaces` on that server. See [Reach kagent-tool-server from another namespace]({{< link path="reference/tools-ecosystem#reach-kagent-tool-server-from-another-namespace" >}}). + +Verify a namespaced install the same way as the default one, plus the negative case: create an Agent in each watched namespace and in one unwatched namespace, and confirm that the controller reconciles only the watched two while the Agent in the unwatched namespace stays unmanaged. + ## Next steps {{< cards >}} From 0b4bb74f8bb57c4e37e9bc18ff3ac93a80bbbe87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20Schmitz=20von=20H=C3=BClst?= Date: Fri, 23 Oct 2026 05:25:46 +0200 Subject: [PATCH 2/2] docs: apply review feedback on the namespaced-install section (#629) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Moritz Schmitz von Hülst --- .../content/kagent/1.x/setup/installation.md | 38 +++++++++++++++++-- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/docs-site/content/kagent/1.x/setup/installation.md b/docs-site/content/kagent/1.x/setup/installation.md index 15765285..37ec24bf 100644 --- a/docs-site/content/kagent/1.x/setup/installation.md +++ b/docs-site/content/kagent/1.x/setup/installation.md @@ -155,15 +155,47 @@ global: watchNamespaces: [prod-agents, staging-agents] ``` -Prefer `global.watchNamespaces` over the per-surface keys. The chart derives both surfaces from this one value, so they cannot disagree: it replaces the cluster-scoped getter and writer ClusterRoles with a Role and RoleBinding in each listed namespace, and it limits the controller's watch to the same list. The chart appends the install namespace to the list automatically, so the release's own objects remain managed. +Prefer `global.watchNamespaces` over the per-surface keys. The chart derives both surfaces from this one value, so they cannot disagree: it replaces every kagent ClusterRole with a Role and RoleBinding in each listed namespace, and it limits the controller's watch to the same list. The chart appends the install namespace to the list automatically, so the release's own objects remain managed. The per-surface keys still exist for installs that scope the surfaces independently, and the validation rules bind them: - `rbac.namespaces` takes precedence over `global.watchNamespaces` whenever the key is present. An explicit `rbac.namespaces: []` restores cluster-scoped RBAC. When `rbac.namespaces` is set, it must include the install namespace or the render fails. -- `config.kubernetes.watchNamespaces` narrows the watch below the RBAC scope. A watch entry that names a namespace outside the RBAC scope fails the render, because the controller would loop on `Forbidden` errors at runtime. +- `config.kubernetes.watchNamespaces` is an explicit watch override. Under `rbac.namespaces`, a watch entry that names a namespace outside the RBAC scope fails the render, because the controller would loop on `Forbidden` errors at runtime. Under `global.watchNamespaces`, the chart folds the entries into the watch scope instead of failing. In both cases the watch list replaces the default, so the install namespace is not added back once `config.kubernetes.watchNamespaces` is set — list it explicitly if the controller should keep watching it. - The `kagent-tool-server` RemoteMCPServer that the chart creates admits references from its own namespace only. An Agent in another watched namespace that binds it does not become ready until you set `allowedNamespaces` on that server. See [Reach kagent-tool-server from another namespace]({{< link path="reference/tools-ecosystem#reach-kagent-tool-server-from-another-namespace" >}}). -Verify a namespaced install the same way as the default one, plus the negative case: create an Agent in each watched namespace and in one unwatched namespace, and confirm that the controller reconciles only the watched two while the Agent in the unwatched namespace stays unmanaged. +Verify a namespaced install the same way as the default one, plus the negative case. The [create your first agent]({{< link path="get-started/your-first-agent" >}}) guide's `kubectl get agents` and conditions checks apply to each namespace; the difference is that an Agent the controller does not watch stays `UNKNOWN` forever. + +1. Create one Agent in each watched namespace. + ```bash + kubectl apply -n prod-agents -f my-agent.yaml + kubectl apply -n staging-agents -f my-agent.yaml + ``` +2. Create one Agent in a namespace the list does not cover. + ```bash + kubectl apply -n dev-agents -f my-agent.yaml + ``` +3. Confirm that the controller reconciles the watched Agents. + ```bash + kubectl get agents -n prod-agents + kubectl get agents -n staging-agents + ``` + Example output: + ```console + NAME AGE READY + my-agent 30s True + my-agent 30s True + ``` +4. Confirm that the Agent in the unwatched namespace stays unmanaged: its `READY` value stays `UNKNOWN`, and its conditions stay empty, because the controller has no watch entry for that namespace. + ```bash + kubectl get agents -n dev-agents + kubectl get agent my-agent -n dev-agents -o jsonpath='{.status.conditions}' + ``` + Example output: + ```console + NAME AGE READY + my-agent 30s UNKNOWN + null + ``` ## Next steps