Skip to content
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ A {{< reuse "kagent-docs/snippets/name-product.md" >}} installation identifies e
- An operator applying a {{< gloss "Harness" >}}Harness{{< /gloss >}} is authenticated by [Kubernetes](#the-kubernetes-plane).
- 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).
- A downstream service can receive a separate [Actor JWT](#downstream-actor-jwts) that identifies the executing Actor.
- The components inside [Agent Substrate](#the-agent-substrate-plane) authenticate each other.

## The Kubernetes plane
Expand Down Expand Up @@ -91,6 +92,12 @@ The controller trusts the issuer in the `config.substrate.actorJwtIssuer` Helm v

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.

### Downstream Actor JWTs {#downstream-actor-jwts}

The egress gateway can also mint an Actor JWT for a downstream model provider, MCP server, or EgressPolicy destination. This token identifies the executing Actor instead of the caller that invoked the agent. A downstream service can authorize the workload without trusting the user's bearer token.

The ModelConfig, RemoteMCPServer, or EgressPolicy names the accepted audiences and the requested lifetime. The `kagent-runtime` audience remains reserved for controller calls and cannot be exported downstream. ModelConfig and RemoteMCPServer credentials are pinned to the revision that a Session uses. EgressPolicy credentials update running and suspended Actors in the same way as other EgressPolicy grants.

## 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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,8 @@ A model provider or an MCP server usually requires an API key or token. The agen

kagent builds these credentials from the following resources.

- Every ModelConfig that sets an `apiKeySecret` becomes a credential on the rule for that model's host.
- Every RemoteMCPServer whose `headersFrom` reads a Secret becomes a credential on the rule for its host.
- Every ModelConfig that sets `spec.apiKeySecret` or `spec.actorJwt` becomes a credential on the rule for that model's host.
- Every RemoteMCPServer whose `spec.headersFrom` reads a Secret, or whose `spec.actorJwt` is set, becomes a credential on the rule for its host.
- Every EgressPolicy destination with an `authentication` block becomes a credential on the rule for its origin.

kagent attaches the credential to the destination's own rule rather than adding a rule for it, and it refuses to compile a credential whose host is not already an allowed destination.
Expand Down
1 change: 1 addition & 0 deletions docs-site/content/kagent/1.x/agents/agent-harness.md
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,7 @@ Some supported combinations still carry restrictions.
| `claude` with `Bedrock` | Does not accept any `bedrock` setting beyond `region`. |
| `Bedrock` on any harness | The Secret must hold an `AWS_BEARER_TOKEN_BEDROCK` key. A Secret of IAM access keys is rejected with `cannot use gateway header injection`, because IAM signs each request locally. |
| `codex` and `claude` with any provider | Neither harness accepts a ModelConfig that sets `defaultHeaders`, `tls`, or `tokenPassthrough`. |
| `spec.actorJwt` on a ModelConfig | Available for `OpenAI`, `Anthropic`, `AzureOpenAI`, `Foundry`, and `Mistral`. Rejected for `Bedrock`, `Gemini`, `Ollama`, `AnthropicVertexAI`, `GeminiVertexAI`, and `SAPAICore`. |

## Tool and skill support

Expand Down
45 changes: 44 additions & 1 deletion docs-site/content/kagent/1.x/agents/egress-policies.md
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,48 @@ The gateway can add a credential from a Kubernetes Secret to each request for an

To rotate the credential, update the Secret. The gateway reads the value from the Secret, so the agent's revision does not change.

## Inject an Actor JWT {#inject-an-actor-jwt}

Use `authentication.actorJwt` in an EgressPolicy when the destination trusts the Agent Substrate issuer and needs the executing Actor's identity. The gateway mints and refreshes the token, and replaces the placeholder header that the client sends.

1. Create an EgressPolicy with an `actorJwt` authentication block for the destination.
```bash
kubectl apply -f - <<EOF
apiVersion: api.kagent.dev/v1alpha3
kind: EgressPolicy
metadata:
name: actor-ticket-api
namespace: kagent
spec:
destinations:
- origin: https://tickets.example.com
authentication:
header: Authorization
prefix: "Bearer "
actorJwt:
audiences:
- tickets-api
expirationSeconds: 900
EOF
```

{{< reuse "kagent-docs/snippets/review-table.md" >}} The gateway sets the header to the prefix followed by the Actor JWT.

| Field | Description |
| ----- | ----------- |
| `authentication.header` | The request header that carries the credential, such as `Authorization` or `X-API-Key`. |
| `authentication.prefix` | Optional. Text that the gateway puts before the token. Include the trailing space, as in `"Bearer "`. |
| `authentication.actorJwt.audiences` | Required. The audiences that the destination accepts. The list must contain 1-16 unique, nonempty values. The `kagent-runtime` audience is reserved for controller calls, so the API server rejects it. |
| `authentication.actorJwt.expirationSeconds` | Optional. The requested token lifetime, from `300` to `3600` seconds. Defaults to `900`. Agent Substrate refreshes the token at the gateway. |

2. Select the policy on the Agent or SandboxTemplate by using the patch command in [Inject a credential](#inject-a-credential). Include `actor-ticket-api` in `egressPolicyRefs`.

3. In the script or SDK that sends the request, send the header with a placeholder value. The gateway replaces only a header that the request already sends. The gateway does not add a header that is missing.
```bash
curl -H 'Authorization: Bearer kagent-credential-injected' \
https://tickets.example.com/tickets
```

## Troubleshoot a grant

The `EgressPoliciesApplied` condition on the Agent or SandboxTemplate reports whether kagent admitted the grants. This condition is independent of `Ready`. An Agent with a missing or invalid policy still reports `Ready`, and it still serves Sessions with its inferred destinations.
Expand All @@ -185,6 +227,7 @@ A grant conflict happens in the following cases:
- Two policies set a different Secret or prefix for the same origin and header.
- A grant sets a credential for an origin that already has a credential from the model or a tool. The credential from the model or the tool always takes precedence.
- A grant sets a credential for an origin that uses token passthrough. Token passthrough and a Secret credential cannot share an origin.
- A grant sets `secretKeyRef` and `actorJwt` in the same authentication block.
- The compiled policy would exceed 256 rules.

In a standalone Sandbox, a destination that no grant allows does not resolve. A client therefore reports a name-resolution error, not an HTTP error. For example, `curl` fails with `Could not resolve host` and exit code `6`. For how to find a denied request in the gateway's log, see [Diagnose a denied request]({{< link path="about/substrate-runtime/networking-and-egress#diagnose-a-denied-request" >}}).
Expand Down Expand Up @@ -217,7 +260,7 @@ The gateway keeps a credential out of the sandbox, but it cannot stop an allowed

2. Delete the EgressPolicy resources and the Secret.
```bash
kubectl delete egresspolicy node-dependencies ticket-api -n kagent
kubectl delete egresspolicy node-dependencies ticket-api actor-ticket-api -n kagent
kubectl delete secret ticket-auth -n kagent
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,38 @@ The egress gateway injects no credential for the provider's host, so the caller'

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.

### Authenticate as the executing Actor {#authenticate-as-the-executing-actor}

Set `spec.actorJwt` to send an Actor JSON Web Token (JWT) to the model provider. Use this option when the provider endpoint is a gateway or service that trusts the Agent Substrate issuer. The endpoint authorizes the agent by its executing Actor instead of by the person who invoked it.

```yaml
apiVersion: api.kagent.dev/v1alpha3
kind: ModelConfig
metadata:
name: actor-authenticated-model
namespace: kagent
spec:
provider: OpenAI
model: example-model
actorJwt:
audiences:
- model-api
expirationSeconds: 900
openAI:
baseUrl: https://models.example.com/v1
```

{{< reuse "kagent-docs/snippets/review-table.md" >}} The gateway sets the provider's credential header to the Actor JWT.

| Field | Description |
| ----- | ----------- |
| `actorJwt.audiences` | Required. The audiences that the destination accepts. The list must contain 1-16 unique, nonempty values. The `kagent-runtime` audience is reserved for controller calls, so the API server rejects it. |
| `actorJwt.expirationSeconds` | Optional. The requested token lifetime, from `300` to `3600` seconds. Defaults to `900`. Agent Substrate refreshes the token at the gateway. |

`actorJwt` is mutually exclusive with `apiKeySecret`, `apiKeySecretKey`, and `tokenPassthrough`. Omit inactive credential fields entirely. The `tokenPassthrough: false` value still counts as setting the field. `defaultHeaders` cannot override the provider credential header that carries the Actor JWT.

The option is available for `OpenAI`, `Anthropic`, `AzureOpenAI`, `Foundry`, and `Mistral`. The API server rejects `actorJwt` for `Bedrock`, `Gemini`, `Ollama`, `GeminiVertexAI`, `AnthropicVertexAI`, and `SAPAICore`. Those providers require AWS credentials, Google credentials, hosted-service credentials, or an OAuth exchange instead of a configurable credential header.

### 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.
Expand Down Expand Up @@ -70,6 +102,7 @@ When a request is made to a provider, the gateway puts the credential in the hea
| `Bedrock` bearer token | `authorization: Bearer <token>` |
| `Ollama` API key, for an Ollama Cloud model | `authorization: Bearer *** |
| `Mistral` API key | `authorization: Bearer *** |
| `ModelConfig` Actor JWT | The provider's credential header |
| A Secret-backed `RemoteMCPServer` header | The header that the server names |

Substrate matches a destination on the exact DNS hostname, without path, port, or scheme. Two credentials that target the same hostname and header are rejected, including a conflict between an agent's model, a memory embedding model, and an MCP server. Give such origins distinct DNS names. A destination given as an IP address cannot carry an injected credential at all.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ kubectl describe modelconfig foundry-model-config -n kagent
| ------------- | ---------- |
| `apiKeySecret` is set | The API key from the referenced Secret. |
| `tokenPassthrough: true` | The bearer token from the caller's request, forwarded to Foundry as the API key. Mutually exclusive with `apiKeySecret`. |
| `actorJwt` is set | An Actor JWT minted by Agent Substrate and sent in the provider's credential header. Mutually exclusive with `apiKeySecret`, `apiKeySecretKey`, and `tokenPassthrough`. |

> [!WARNING]
> **Azure Workload Identity is not currently supported.** In earlier versions of kagent, omitting the credential fields fell back to Azure Workload Identity, which depended on an annotated ServiceAccount on the agent's pod. An agent now runs as a Substrate Actor rather than as a pod that kagent controls, so there is no per-agent ServiceAccount to federate an Azure identity onto. Supply an API key instead.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ A link reaches Azure regardless of its media type. Inline bytes reach Azure as a
| `apiKeySecret` is set | The API key from the referenced Secret. |
| `azureOpenAI.azureAdToken` is set | The Microsoft Entra ID token given in the field. |
| `tokenPassthrough: true` | The bearer token from the caller's request, forwarded to Azure OpenAI as the API key. This is not Microsoft Entra ID authentication, and it is mutually exclusive with `apiKeySecret`. |
| `actorJwt` is set | An Actor JWT minted by Agent Substrate and sent in the `api-key` header. This is not Microsoft Entra ID authentication, and it is mutually exclusive with `apiKeySecret`, `apiKeySecretKey`, and `tokenPassthrough`. |

> [!WARNING]
> **Azure Workload Identity is not currently supported.** In earlier versions of kagent, omitting the credential fields fell back to Azure Workload Identity, which depended on an annotated ServiceAccount on the agent's pod. An agent now runs as a Substrate Actor rather than as a pod that kagent controls, so there is no per-agent ServiceAccount to federate an Azure identity onto. Supply a credential with one of the configurations in the preceding table.
Expand Down
27 changes: 27 additions & 0 deletions docs-site/content/kagent/1.x/skills-and-mcp/about-tools.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,33 @@ spec:

Every model and MCP server that an agent reaches at the server's origin must also forward the caller's token. Otherwise, the Agent reports the `Compatible` condition as `False`. For more information, see [Forward the caller's token]({{< link path="setup/model-providers/about-model-providers#forward-the-callers-token" >}}).

### Actor JWT authentication {#actor-jwt-authentication}

An MCP server can authenticate the executing Actor instead of the caller. Set `spec.actorJwt` on the RemoteMCPServer when the server trusts the Agent Substrate issuer and authorizes requests by Actor identity.

```yaml
apiVersion: api.kagent.dev/v1alpha3
kind: RemoteMCPServer
metadata:
name: actor-mcp-server
namespace: kagent
spec:
description: An MCP server that authenticates the executing Actor.
url: https://mcp.example.com/mcp
protocol: STREAMABLE_HTTP
actorJwt:
audiences:
- mcp-api
expirationSeconds: 900
```

| Field | Description |
| ----- | ----------- |
| `actorJwt.audiences` | Required. The audiences that the server accepts. The list must contain 1-16 unique, nonempty values. The `kagent-runtime` audience is reserved for controller calls, so the API server rejects it. |
| `actorJwt.expirationSeconds` | Optional. The requested token lifetime, from `300` to `3600` seconds. Defaults to `900`. Agent Substrate refreshes the token at the gateway. |

The agent sends the token as `Authorization: Bearer <token>`. A RemoteMCPServer cannot set both `actorJwt` and `tokenPassthrough`, and `headersFrom` cannot set its own `Authorization` header when `actorJwt` is enabled. The controller cannot authenticate as an Actor for global tool discovery. These tools do not appear in the UI tool picker. Agents discover the tools at runtime with their own identity.

## Subagents as tools

A `subAgent` binding points at another AgentTemplate in the same namespace so that one agent can route work to another agent. The model reads the `description` when it decides whether to route work here. A description that states plainly what the bound agent is for therefore matters more than the detail of its configuration.
Expand Down