# Install the agent on Kubernetes

In a Kubernetes cluster the agent is installed with one Helm chart. It describes the cluster's workloads, services and how they connect, and reports them to one Catalog environment. Before you start, create the environment in Catalog and an enrollment token for it; see [Enrollment](https://docs.evohub.io/agent-overview.md#enrollment).

## Install

The console's **New agent** dialog shows these commands with your token, version and agent name filled in.

### Keep the token out of shell history

Paste the token into a Secret, then point the chart at it:

```bash
kubectl create namespace evohub
kubectl -n evohub create secret generic evohub-agent-enrollment --from-file=token=/dev/stdin
# paste the token, press Enter, then Ctrl-D
helm install evohub-agent oci://ghcr.io/evolutionarysynchronization/charts/evohub-agent \
  --version 26.10.1 -n evohub --set enrollment.existingSecret=evohub-agent-enrollment
```

### Or in one command

```bash
helm install evohub-agent oci://ghcr.io/evolutionarysynchronization/charts/evohub-agent \
  --version 26.10.1 -n evohub --create-namespace --set enrollment.token=evohub_enroll_...
```

The token then also lands in your shell history and in Helm's release record (a `sh.helm.release.v1.*` Secret in the namespace). It is single-use and spent once the agent enrolls.

Always pass `--version`. No `latest` chart or image is published, and each chart version pins its image by digest.

To set how the agent is shown in EvoHub, add `--set agentName=<name>`; without it, EvoHub shows the pod's hostname.

### Check that it runs

```bash
kubectl -n evohub logs deploy/evohub-agent-catalog
```

The agent logs `enrolled`, then its sweeps. What it found appears in Catalog within a few minutes, and the agent shows as **Reporting** under **Organization → Agents**. If EvoHub refuses it, the log entry's `action` field says what to do.

The names on this page assume the release name `evohub-agent`. With another release name, the ServiceAccount, Secret and Deployment names change accordingly.

## Verify a release

Images and charts are signed with Sigstore, keyless, by the agent's release workflow. Images also carry an SBOM and build provenance.

```bash
cosign verify ghcr.io/evolutionarysynchronization/evohub-agent:26.10.1 \
  --certificate-identity-regexp '^https://github.com/EvolutionarySynchronization/evohub-agent/\.github/workflows/release\.yml@refs/tags/v' \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com
```

## What it is allowed to do

The chart creates one ServiceAccount, `evohub-agent-catalog`, with exactly these grants:

| Grant | On | Why |
| --- | --- | --- |
| `get`, `list`, cluster-wide | `namespaces`, `nodes`, `services`, `persistentvolumeclaims`, `deployments`, `statefulsets`, `daemonsets`, `cronjobs`, `ingresses`, `horizontalpodautoscalers` | What it inventories |
| `list`, cluster-wide, only while live state is on (the default) | `pods` | How many run and are ready, which node each is on, and why one fails |
| `get`, `update`, in its own namespace | One Secret, by name: `evohub-agent-catalog-identity` | Where it keeps its own key |

That is all:

- **No access to your Secrets.** It cannot list Secrets, read any Secret other than its own, or create one.
- No access to ConfigMaps, and no `watch`: it lists in full sweeps.
- No write access to anything except its own Secret.
- The ServiceAccount token is mounted into the agent container only. Containers you add with `extraContainers` share the pod and its network, but not the token.

Reading those kinds means the agent reads each object in full, in memory, inside your cluster, including container environment variable values. Those values are removed before anything is sent and are never written anywhere. See [What the agent sends](https://docs.evohub.io/agent-data-and-redaction.md).

The pod runs as UID 65532, non-root, with a read-only root filesystem, all capabilities dropped, no privilege escalation and the `RuntimeDefault` seccomp profile. The image is distroless and has no shell. If your platform assigns UIDs itself, for example OpenShift, override `podSecurityContext`.

## What it keeps in your cluster

- `Secret/evohub-agent-catalog-identity` holds the agent's private key and ID. It is protected the way your Secrets are, including by etcd encryption if you have it. Do not delete it: it is the agent's identity.
- `Secret/evohub-agent-enrollment` holds the spent enrollment token, if you created it. You can delete it after the first start.

The agent keeps one hash per object between sweeps, and lists objects 100 at a time. The chart sets a 256 MiB memory limit.

## Chart values

| Value | Default | |
| --- | --- | --- |
| `apiUrl` | `https://api.evohub.io` | EvoHub's API, HTTPS only |
| `enrollment.token` | empty | The single-use token, if you pass it inline |
| `enrollment.existingSecret` | empty | A Secret holding the token, under `enrollment.existingSecretKey` (`token`) |
| `agentName` | the pod's hostname | How the agent appears in EvoHub |
| `catalog.sweepInterval` | `5m` | How often to run a full sweep |
| `catalog.heartbeatInterval` | `1m` | How often to send a heartbeat |
| `catalog.liveState.enabled` | `true` | Report how workloads are running now; needs the `pods` grant, which is created only while this is on |
| `catalog.liveState.interval` | `1m` | How often to report live state |
| `catalog.resources` | requests 20m CPU, 64Mi; limits 500m CPU, 256Mi | The agent container's resources |
| `extraEnv` | `[]` | Extra environment, for example `HTTPS_PROXY` and `NO_PROXY` |
| `image.digest` | empty | Pin the image by digest as well |
| `podSecurityContext`, `securityContext` | non-root, read-only, all capabilities dropped | Override only if your platform requires it |
| `nodeSelector`, `tolerations`, `affinity`, `priorityClassName`, `podAnnotations`, `podLabels`, `imagePullSecrets` | empty | The usual scheduling and metadata settings |
| `extraContainers` | `[]` | Sidecars your platform requires; the agent needs none |

## Upgrade

```bash
helm upgrade evohub-agent oci://ghcr.io/evolutionarysynchronization/charts/evohub-agent \
  --version <new> -n evohub --reset-then-reuse-values
```

- **Review the RBAC first.** An upgrade can change what the agent may read. Check with `helm template` or the `helm diff` plugin before applying.
- **Its key and ID are kept.** Never use `helm upgrade --force`: it replaces the identity Secret and the agent loses its key.
- The agent never upgrades itself, and reports its version on every heartbeat.

## Uninstall

```bash
helm uninstall evohub-agent -n evohub
```

This removes everything the chart created, including the agent's key and the cluster-wide ClusterRole and ClusterRoleBinding. It leaves the namespace and any Secret you created yourself.

Then revoke the agent under **Organization → Agents**; until you do, it stays enrolled. The resources it reported stay in Catalog and are shown as stale. Installing again enrolls a new agent.

## Outside the cluster

The same binary can describe a Kubernetes cluster from a VM or bastion host under systemd, given a kubeconfig. Then its permissions are whatever the kubeconfig allows: give it a read-only ServiceAccount token bound to the same ClusterRole as the chart. Exec-plugin authentication, such as `aws` or `gcloud`, does not work in the unit's sandbox. To describe the VM itself instead, see [Install on a VM](https://docs.evohub.io/agent-vm.md).

## Related

- [EvoHub Agent overview](https://docs.evohub.io/agent-overview.md)
- [What the agent sends](https://docs.evohub.io/agent-data-and-redaction.md)
- [Get started with Catalog](https://docs.evohub.io/catalog-getting-started.md)
