diff --git a/docs-site/content/kagent/1.x/about/substrate-runtime/identity.md b/docs-site/content/kagent/1.x/about/substrate-runtime/identity.md index 5f5d3598..d1aed138 100644 --- a/docs-site/content/kagent/1.x/about/substrate-runtime/identity.md +++ b/docs-site/content/kagent/1.x/about/substrate-runtime/identity.md @@ -7,15 +7,16 @@ aliases: - /kagent/1.x/substrate-runtime/identity/ --- -A {{< reuse "kagent-docs/snippets/name-product.md" >}} installation identifies three different kinds of callers, and each one is handled by a different system. +A {{< reuse "kagent-docs/snippets/name-product.md" >}} installation identifies each kind of caller with a different system. - An operator applying a {{< gloss "Harness" >}}Harness{{< /gloss >}} is authenticated by [Kubernetes](#the-kubernetes-plane). -- A caller that reaches [kagent's own gRPC API](#the-kagent-control-plane) is assigned a principal by kagent, both for a {{< gloss "Session" >}}Session{{< /gloss >}} and for the resources that the API writes. +- A caller that reaches [kagent's gRPC API](#the-kagent-control-plane) is assigned a principal by kagent, both for a {{< gloss "Session" >}}Session{{< /gloss >}} and for the resources that the API writes. +- An agent's runtime calls the same API with a [token that Agent Substrate issues](#agent-runtime-calls). - The components inside [Agent Substrate](#the-agent-substrate-plane) authenticate each other. ## The Kubernetes plane -Kubernetes role-based access control (RBAC) governs who can create, read, edit, or delete Kubernetes resources with `kubectl`, including Harness, AgentTemplate, and {{< gloss "Agent" >}}Agent{{< /gloss >}} resources. A cluster's existing roles and bindings decide who authors an agent's runtime, its behavior, and the pairing of the two on that path. +Kubernetes role-based access control (RBAC) governs who can create, read, edit, or delete Kubernetes resources with `kubectl`, including Harness, AgentTemplate, and {{< gloss "Agent" >}}Agent{{< /gloss >}} resources. A cluster's existing roles and bindings decide who authors an agent's Harness and AgentTemplate, and the pairing of the two on that path. Because neither a Harness nor an AgentTemplate names the other, the Agent resource carries the pairing, and RBAC on Agents governs it. Whoever can write Agents in a namespace controls which template runs on which Harness, even without edit access to the Harnesses and AgentTemplates that those Agents name. For more information about the pairing, see the [Agent core concept]({{< link path="about/core-concepts#agent" >}}). @@ -44,14 +45,14 @@ Any other value fails controller startup rather than falling back to a default. > [!WARNING] > **`insecure` lets a caller select its own principal.** It verifies neither the header nor the query parameter, and the same authenticator guards the controller's `/mcp` endpoint. Treat the principal on a call as a label that the caller chose, and restrict network access to both endpoints rather than relying on it. -Authorization is separate from both modes. kagent installs an authorizer that permits every check regardless of the authentication mode, so neither mode constrains what an authenticated caller may do. +Authorization is separate from both modes. For user calls, kagent installs an authorizer that permits every check regardless of the authentication mode, so neither mode constrains what an authenticated user can do. For agent runtime calls, kagent applies a fixed policy that limits each Actor to its own resources. For more information, see [Agent runtime calls](#agent-runtime-calls). ### Resources that the API writes kagent's gRPC API reaches the same resources by a second path. The AgentTemplate service creates, updates, and deletes AgentTemplates, and the Harness service creates and deletes Harnesses, both through the kagent controller. The `kagent apply -f` command calls the AgentTemplate service, and any client that reaches the gRPC endpoint can call either service. The controller writes these resources with its own service account rather than the caller's, so Kubernetes RBAC never evaluates the caller. The authenticator above resolves the caller instead. > [!WARNING] -> By default, kagent neither authenticates nor authorizes this path. The `insecure` authenticator admits every request, and the authorizer it installs permits every check. Any caller that reaches the gRPC endpoint can author an agent's runtime and behavior, regardless of their Kubernetes permissions. Do not expose port `8083` outside the cluster. For the identity that this mode gives an anonymous caller, and for the mode that changes it, see [Controller authentication modes](#controller-authentication-modes). +> By default, kagent neither authenticates nor authorizes this path. The `insecure` authenticator admits every request, and the authorizer it installs permits every check. Any caller that reaches the gRPC endpoint can author an agent's Harness and AgentTemplate, regardless of their Kubernetes permissions. Do not expose port `8083` outside the cluster. For the identity that this mode gives an anonymous caller, and for the mode that changes it, see [Controller authentication modes](#controller-authentication-modes). ### Creator ownership @@ -74,6 +75,22 @@ The returned `SessionShare.expiresAt` field records when the token stops grantin A share widens what the holder can reach to what the owner can see, and the underlying record is read as the owner rather than as the visitor. Revoking the share withdraws that access. +### Agent runtime calls + +An agent's runtime calls the controller to store task state and memories, and to read its own Agent and Session. These calls arrive on the same port as user calls, `8083`. However, the controller authenticates agent runtime calls separately from the user authentication mode. The task state and memory calls that only an agent makes always need a valid token, even in `insecure` mode. The controller treats a call that sends no `Authorization` header as a user call, and applies the user authentication mode to it. + +The runtime, the Agent Substrate egress gateway, and the controller each take one step to authenticate a call. + +1. The runtime sends a placeholder bearer token in the `Authorization` header. +2. The Agent Substrate egress gateway replaces the placeholder with a JSON Web Token (JWT) that identifies the {{< gloss "Actor" >}}Actor{{< /gloss >}}. The token is valid for five minutes, and its audience is `kagent-runtime`. The gateway adds the token only to calls to the controller. Actor snapshots do not store the token. +3. The controller verifies the token's signature, issuer, audience, and lifetime, and allows 30 seconds of clock skew. The controller then binds the token to the Actor's current Session. The controller rejects a token that fails verification, and does not fall back to user authentication. + +An authenticated Actor can read its own Agent and use its own task state and memories. The Actor can also read, update, and send messages to its own Session. The Actor cannot create or delete a Session, use a share, reach any other Session, or act with its owner's permissions. That restriction includes other Sessions of the same agent and the same owner. + +The controller trusts the issuer in the `config.substrate.actorJwtIssuer` Helm value, which must match the `ateApi.actorJWTIssuer` value of the Agent Substrate chart. Both values default to `https://idp.ate-system.svc`, so change them only together. The controller fetches the issuer's signing keys at startup, and a controller that cannot reach the issuer does not start. + +Upgrade the controller and the runtime images together. An Actor snapshot from an earlier runtime image still sends an identity header that the controller no longer accepts, so you must recreate that snapshot after an upgrade. + ## The Agent Substrate plane {{< gloss "Agent Substrate" >}}Agent Substrate{{< /gloss >}} authenticates its own components rather than authenticating end users. Its API server accepts Kubernetes ServiceAccount tokens issued for its audience, and the components that carry traffic to an Actor authenticate each other with mutual Transport Layer Security (mTLS). The [kagent installation guide]({{< link path="setup/installation" >}}) covers creating the certificate authority pools and the JSON Web Token (JWT) authority pool that these identities are issued from, which is a required step that no Helm chart performs. diff --git a/docs-site/content/kagent/1.x/agents/agent-memory.md b/docs-site/content/kagent/1.x/agents/agent-memory.md index 898fb5b3..fd966945 100644 --- a/docs-site/content/kagent/1.x/agents/agent-memory.md +++ b/docs-site/content/kagent/1.x/agents/agent-memory.md @@ -206,7 +206,7 @@ Each memory is one row in the `memory` table, which the vector migration created | Column | What it holds | | ------ | ------------- | | `content` | The text that the agent saved. Retrieval returns it to a later conversation. | - | `agent_name` | The agent that owns the memory, written as `__NS__` with every hyphen replaced by an underscore. The name identifies one {{< gloss "Agent" >}}Agent{{< /gloss >}}, so the same AgentTemplate paired into two Agents owns two separate sets of memories. | + | `agent_name` | The agent that owns the memory, written as `/`, such as `kagent/my-first-agent`. The name identifies one {{< gloss "Agent" >}}Agent{{< /gloss >}}, so the same AgentTemplate paired into two Agents owns two separate sets of memories. A memory that an earlier release saved keeps its original key, such as `kagent__NS__my_first_agent`, and kagent does not migrate it. The agent no longer retrieves that memory. To list or delete it, pass the original key. | | `user_id` | The user that the memory belongs to. | | `embedding` | The 768-dimensional vector that similarity search compares a query against. | | `created_at` and `expires_at` | When kagent wrote the memory, and `ttlDays` after that. | @@ -216,28 +216,28 @@ Each memory is one row in the `memory` table, which the vector migration created The SQL query only reads the table. To list or clear memories, call the `MemoryService` that the kagent controller serves over gRPC. -No CLI command manages memories, so these examples call the service with [grpcurl](https://github.com/fullstorydev/grpcurl). Both calls take the `agent_name` exactly as the memory table stores it. Be sure to copy the value out of the table rather than assembling it by hand, because an incorrect name fails silently. +No CLI command manages memories, so these examples call the service with [grpcurl](https://github.com/fullstorydev/grpcurl). Each call acts on the memories of the user that the call authenticates as, and does not take a user ID. For how the controller resolves that user, see [Controller authentication modes]({{< link path="about/substrate-runtime/identity#controller-authentication-modes" >}}). In the default `insecure` mode, a call without an identity runs as `admin@kagent.dev`. In that case, the call returns an empty list for memories that belong to any other user, and a Delete call succeeds without deleting anything. To act on another user's memories in `insecure` mode, add `-H 'X-User-Id: '` to each grpcurl call, with the value from the `user_id` column. + +Each call also takes the `agent_name` exactly as the memory table stores it. An incorrect name fails silently, so copy the value from the table instead of typing it by hand. 1. Port-forward the controller's gRPC port, and confirm that your kagent installation sets `config.server.grpcReflection=true`. ```bash kubectl port-forward -n kagent svc/kagent-controller 8083:8083 ``` -2. List the memories that an agent stores for one user. +2. List the memories that an agent stores. ```bash grpcurl -plaintext -d '{ - "agent_name": "kagent__NS__my_first_agent_my_first_harness", - "user_id": "admin@kagent.dev" + "agent_name": "kagent/my-first-agent" }' localhost:8083 kagent.runtime.v1.MemoryService/List ``` Each entry returns the `id`, `content`, `access_count`, `created_at`, and `expires_at` fields. Results are ranked by how often retrieval has returned them, so the most-used memories appear first. -3. Delete the memories for an agent and user. +3. Delete the memories that an agent stores. ```bash grpcurl -plaintext -d '{ - "agent_name": "kagent__NS__my_first_agent_my_first_harness", - "user_id": "admin@kagent.dev" + "agent_name": "kagent/my-first-agent" }' localhost:8083 kagent.runtime.v1.MemoryService/Delete ``` diff --git a/docs-site/content/kagent/1.x/examples/skills.md b/docs-site/content/kagent/1.x/examples/skills.md index ba146616..e5267624 100644 --- a/docs-site/content/kagent/1.x/examples/skills.md +++ b/docs-site/content/kagent/1.x/examples/skills.md @@ -20,39 +20,44 @@ Because kagent fetches a skill this late, review the following considerations. * **Each Actor mounts up to 29 distinct skill images.** Images shared by several skills or plugins count once. A revision that needs more fails when kagent builds its ActorTemplate. * **Scripts run in the runtime image.** A skill's scripts get whatever the Harness image provides. The kagent runtime image is Alpine Linux with `bash`, `git`, and the standard Alpine utilities, and it does **not** include Python. -### Skill tools +### Skill tools and built-in tools -On a `kagent` Harness, attaching a skill adds seven tools to the agent, whether or not the skill ships a script. The first three read skills, and the rest let the agent act on their files. The `claude` and `codex` Harness runtimes take the same skills and expose them through their own coding agent's tools instead. +On a `kagent` Harness, every agent has kagent's built-in tools, even without a skill. A skill runs its scripts with the built-in `bash` tool. Attaching a skill also adds the skill tools, which read skills. The `claude` and `codex` Harness runtimes also take skills. They expose the skills through their own coding agent's tools. -| Tool | What it does | -| ---- | ------------ | -| `list_skills` | Lists the attached skills with their names and descriptions. | -| `load_skill` | Reads a skill's full `SKILL.md` instructions. | -| `load_skill_resource` | Reads one file inside a skill directory, such as a reference document. | -| `read_file` | Reads a file from the skills directory or the session directory. | -| `write_file` | Writes a file to the session directory. | -| `edit_file` | Replaces an exact string in a file that the agent has already read. | -| `bash` | Runs a shell command in the session directory `/tmp/kagent//`. Commands time out after 30 seconds. | +| Tool | Availability | What it does | +| ---- | ------------ | ------------ | +| `list_skills` | With a skill | Lists the attached skills with their names and descriptions. | +| `load_skill` | With a skill | Reads a skill's full `SKILL.md` instructions. | +| `load_skill_resource` | With a skill | Reads one file inside a skill directory, such as a reference document. | +| `read_file` | Built-in | Reads a file, with line numbers. | +| `write_file` | Built-in | Writes a file, and creates its parent directories. | +| `edit_file` | Built-in | Replaces an exact string in a file. The edit fails when the string occurs more than once, unless the agent asks to replace every occurrence. | +| `list_files` | Built-in | Lists the contents of a directory. | +| `grep_file` | Built-in | Searches a file, or a directory recursively, for a regular expression. | +| `bash` | Built-in | Runs a command in a new shell. A command stops after 60 seconds by default. The agent can raise that limit to 600 seconds for one call. The tool combines standard output and standard error, and truncates the result at 64 KiB. When the call returns, the tool stops every process that the command started. | -The `config.runtimeDefaults.skillTools` Helm values change this tool set for every agent in the installation, because a Harness has no per-agent equivalent. +The built-in tools accept an absolute path or a path relative to the workspace, `/data/workspace`. The root agent and its subagents share the workspace. The workspace starts empty, and its files survive when the Actor is [suspended and resumed]({{< link path="about/substrate-runtime/suspend-and-resume#what-a-snapshot-captures" >}}). A command that is still running does not survive. + +A Harness does not have a per-agent setting for the built-in tools. The `config.runtimeDefaults.builtinTools` Helm values change the built-in tools for every agent in the installation. ```yaml config: runtimeDefaults: - skillTools: - fileSearchTools: true + builtinTools: + disableFileSearch: true disableBash: true ``` | Setting | Description | | ------- | ----------- | -| `fileSearchTools` | Set to `true` to add `list_files`, which lists the contents of a directory, and `grep_file`, which searches file contents for a pattern and can recurse. Both tools reach only the skills and session roots that `read_file` reads. These settings are off by default because they give an agent broad visibility of the filesystem. When `bash` is disabled, these tools keep that visibility available to the agent. | -| `disableBash` | Set to `true` to remove the `bash` tool from the root agent and from every subagent. The agent still loads skills and keeps the file tools. Defaults to `false`. | +| `disableFileSearch` | Set to `true` to remove `list_files` and `grep_file` from the root agent and from every subagent. Defaults to `false`. This value replaces `skillTools.fileSearchTools` from earlier releases, with the opposite meaning. | +| `disableBash` | Set to `true` to remove the `bash` tool from the root agent and from every subagent. The agent keeps the file tools, and still loads skills. Defaults to `false`. | +| `bashVenvPath` | The path to a Python virtual environment in the Harness image. The `bash` tool adds the environment's `bin` directory to the start of `PATH`, and sets `VIRTUAL_ENV`. The kagent runtime image does not include Python. To use this setting, build a Harness image that includes the environment. Defaults to empty. | Attaching a skill also changes what the agent is told. The runtime appends the name and description of every attached skill to the model request, along with an instruction to call `load_skill` before acting on one, so a skill reaches the model even before any tool is called. > [!IMPORTANT] -> The `bash` tool gives the agent shell access inside its own Actor sandbox, and the sandbox is the boundary that contains it. Review a skill before you attach it, and treat the [egress]({{< link path="about/substrate-runtime/sandboxing" >}}) that the Actor is granted as the reach that the skill has. Writes are confined to the session directory, so a skill cannot modify `/skills` or another skill's files. +> The `bash` tool gives the agent shell access inside its own Actor sandbox, and the sandbox is the boundary that contains the agent. The file tools are not limited to the workspace. They read and write anywhere in the sandbox, including `/skills`, so one skill's instructions can change another skill's files. Review a skill before you attach it, and treat the [egress]({{< link path="about/substrate-runtime/sandboxing" >}}) that you grant the Actor as the reach that the skill has. ## Before you begin diff --git a/docs-site/content/kagent/1.x/get-started/your-first-mcp-tool.md b/docs-site/content/kagent/1.x/get-started/your-first-mcp-tool.md index 431331f3..90f6c864 100644 --- a/docs-site/content/kagent/1.x/get-started/your-first-mcp-tool.md +++ b/docs-site/content/kagent/1.x/get-started/your-first-mcp-tool.md @@ -39,6 +39,14 @@ kagent records what it discovered on the server's status, so the tool names come > [!NOTE] > The built-in server is installed only when the `kagent-tools.enabled` Helm value is `true`, which is the default. If the command does not return any resources, either re-install with that value enabled, or use your own server as described in [Bind your own MCP server](#bind-your-own-mcp-server). + > [!WARNING] + > At v1.0.0-alpha17, an agent's sandbox cannot resolve the short Service name in the built-in server's URL. Every tool call then fails with a `no such host` error. For more information, see [kagent#3315](https://github.com/kagent-dev/kagent/issues/3315). Until a fix ships, run the following command to point the server at the fully qualified Service name. The kagent Helm chart owns the `kagent-tool-server` resource, and a `helm upgrade` reverts the patch, so run the command again after each upgrade. + + ```bash + kubectl patch remotemcpserver kagent-tool-server -n kagent --type=merge \ + -p '{"spec":{"url":"http://kagent-tools.kagent.svc.cluster.local:8084/mcp"}}' + ``` + 2. Re-apply the `my-first-template` AgentTemplate with a `spec.tools` list and a system prompt that tells the model what the tools are for. ```yaml kubectl apply -f - < [!CAUTION] -> Uninstalling deletes every kagent resource in every namespace, along with all agent conversation state and all stored snapshots. None of it can be recovered afterward. Back up anything you want to keep before you start. - -## Uninstall kagent - -Remove the kagent release before the CRDs release, because deleting the definitions first strands the controller. - -1. Uninstall the kagent chart. +2. Confirm that your kubeconfig points at the cluster that you want to uninstall from. The `kagent uninstall` command does not ask for confirmation, and it acts on the current context. ```bash - helm uninstall kagent -n kagent + kubectl config current-context ``` +3. Back up any Harness, AgentTemplate, and ModelConfig definitions that you want to keep. +4. Confirm that nothing outside kagent depends on the agents that you are removing. -2. Uninstall the CRDs chart. This step deletes every `kagent.dev` custom resource definition, and Kubernetes deletes every resource of those kinds across all namespaces with them. - ```bash - helm uninstall kagent-crds -n kagent - ``` - -> [!NOTE] -> The `kagent uninstall` command is a convenience path that removes the same two kagent releases in the same order. It does not remove Agent Substrate or the development PostgreSQL resources that `kagent install` created, so the rest of this page still applies. - -## Uninstall Agent Substrate - -Agent Substrate is a separate installation in the `ate-system` namespace, and no kagent command removes it. +> [!CAUTION] +> Uninstalling deletes every kagent resource in every namespace, along with all agent conversation state in the development database and all stored snapshots. You cannot recover any of it afterward. Back up anything you want to keep before you start. -1. Uninstall the Agent Substrate chart. - ```bash - helm uninstall substrate -n ate-system - ``` +## Uninstall kagent -2. Uninstall the Agent Substrate PodCertificate chart. - ```bash - helm uninstall substrate-podcert -n podcertificate-controller-system - ``` +Uninstall kagent with the kagent CLI. The command waits for each Helm release to finish its removal before it continues. -3. Uninstall the Agent Substrate CRDs chart, which deletes the `workerpools`, `sandboxconfigs`, and `csidriverconfigs` definitions. +1. Uninstall kagent. When kagent runs in a namespace other than `kagent`, add the `--namespace` flag with that namespace. If the namespace does not match, the command stops before it deletes anything, and prints the namespace to use. ```bash - helm uninstall substrate-crds -n ate-system + kagent uninstall ``` -## Remove the identity material + Example output: + ```console + kagent uninstalled successfully -The `kagent install` command created the identity pools outside Helm, so no release owns them and no uninstall removes them. A later install that tries to create a pool that already exists fails with a message naming the Secret. - -```console -Error: while uploading pool state to secret: secrets "actor-id-jwt-pool" already exists -``` - -1. Delete the actor identity pools and the material derived from them. - ```bash - kubectl delete secret actor-id-jwt-pool actor-id-ca-pool actor-id-ca-certs -n ate-system - kubectl delete configmap ate-api-authentication -n ate-system + The kagent namespace stays, with anything else in it. To delete it and everything it holds: + kubectl delete namespace kagent ``` -2. Delete the service DNS, pod identity, and PostgreSQL CA pools. - ```bash - kubectl delete secret -n podcertificate-controller-system \ - service-dns-ca-pool pod-identity-ca-pool postgres-ca-pool - ``` + When a step fails, the command exits with a non-zero status. Run the command again to finish the uninstall. Each step skips what is already gone, and prints a warning for a release that no longer exists. -3. Delete the cluster-scoped trust bundles that publish the Agent Substrate certificate authorities. Deleting a namespace does not remove a ClusterTrustBundle. A later install that finds a stale bundle might start Agent Substrate components that trust the old authority. Every agent turn then fails with `invalid peer certificate: UnknownIssuer`. +2. Optional: Delete the namespace that kagent ran in, such as `kagent`, and everything that remains in it, such as Secrets that you created for your agents. ```bash - kubectl delete clustertrustbundle \ - egress-mitm.ate.dev:mitm:primary-bundle \ - podidentity.podcert.ate.dev:identity:primary-bundle \ - postgres.podcert.ate.dev:identity:primary-bundle \ - servicedns.podcert.ate.dev:identity:primary-bundle + kubectl delete namespace kagent ``` -4. Delete the development PostgreSQL resources that `kagent install` created. +3. Confirm that no kagent or Agent Substrate definitions remain. ```bash - kubectl delete -n kagent \ - deployment/kagent-postgresql \ - service/kagent-postgresql \ - configmap/kagent-postgresql \ - secret/kagent-postgres-owner \ - secret/kagent-postgres-readwrite \ - pvc/kagent-postgresql \ - --ignore-not-found + kubectl get crd | grep -E 'kagent\.dev|ate\.dev' + kubectl get ns ate-system podcertificate-controller-system ``` -> [!IMPORTANT] -> Deleting the development PostgreSQL PersistentVolumeClaim removes all conversation state that the development install stored. Back up anything that you need before you delete it. - -## Remove the namespaces - -Deleting the namespaces removes anything that the preceding steps missed, including volumes left by optional components. - -```bash -kubectl delete namespace kagent ate-system podcertificate-controller-system -``` - -Confirm that nothing remains. - -```bash -kubectl get crd | grep -E 'kagent\.dev|ate\.dev' -kubectl get ns kagent ate-system podcertificate-controller-system -``` - -The first command prints nothing. The second reports that it found none of the three namespaces. + The first command prints nothing. The second reports that it found neither namespace. diff --git a/docs-site/content/kagent/1.x/setup/model-providers/about-model-providers.md b/docs-site/content/kagent/1.x/setup/model-providers/about-model-providers.md index ea3b7c56..b1c15636 100644 --- a/docs-site/content/kagent/1.x/setup/model-providers/about-model-providers.md +++ b/docs-site/content/kagent/1.x/setup/model-providers/about-model-providers.md @@ -28,6 +28,8 @@ Set `tokenPassthrough: true` to send the bearer token from the incoming agent-to The egress gateway injects no credential for the provider's host, so the caller's token reaches the provider unchanged. kagent also refuses to attach a static credential to that host. +Every model and MCP server that an agent reaches at the provider's origin must also forward the caller's token. Otherwise, the Agent reports the `Compatible` condition as `False`, with a message such as `destination "https://api.openai.com:443" mixes tokenPassthrough with clients that do not use it`. The condition is also `False` when a model or MCP server at that origin sets its own value for the header that the token replaces. + ### Turn off model streaming The `kagent` harness streams its calls to the model by default, including OpenAI Chat Completions and Responses. An endpoint that rejects a streaming request fails every turn, so set `stream: false` on the ModelConfig to call it without streaming.