> ## Documentation Index
> Fetch the complete documentation index at: https://docs.kguardian.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# profile

> Show a workload's security posture, compare profile revisions, and print a recommended securityContext patch

## Synopsis

Read-only views over the broker's workload security profiles. A profile is
keyed by workload (`namespace/kind/name`), not by pod. It joins what
kguardian observed: Pod Security Standards checks against the reported
`securityContext`, network peers and audit verdicts, syscalls and any
`SeccompProfile` CR, running image digests, and compute settings.

```bash theme={null}
kubectl kguardian profile get <namespace>/<kind>/<name> [-o table|json|yaml]
kubectl kguardian profile list [flags]
kubectl kguardian profile diff <namespace>/<kind>/<name> [--from N] [--to N]
kubectl kguardian profile export <namespace>/<kind>/<name> --format pss
```

`kind` is case-sensitive and is the owning workload's kind: `Deployment`,
`StatefulSet`, `DaemonSet`, `CronJob`, `Job`, or `Pod` for a pod with no
owner. A ReplicaSet's pods are profiled under their Deployment.

<Warning>
  **unknown is not a pass.** When kguardian has no data for a dimension, or the
  source is not configured (vulnerability scanning, signatures), its status is
  `unknown`, and a readiness check it cannot evaluate prints `can't tell`. There
  is no numeric score: status is a tier (`ok`, `warn`, `risk`, `unknown`)
  derived from findings. Posture is `ok` only when all four core dimensions
  are known and ok, and images stay `unknown` until vulnerability data exists,
  so today no workload reads `ok`. Always read the status with its coverage.
</Warning>

## profile get

Calls `GET /workloads/{namespace}/{kind}/{name}/profile`, which the broker
computes live.

```
Workload:   payments/Deployment/checkout
Live pods:  2
Posture:    warn  coverage 25% (known core dimensions)
Unknown:    network, podSecurity, images (no data; not counted as ok or risk)
  network unknown: No audit policy covers this workload; applied NetworkPolicies are not visible to the broker
  syscalls warn: No SeccompProfile CR enforces the observed syscall set
  podSecurity unknown: Every evaluated check passes restricted, but 9 checks cannot be seen (e.g. hostPath), so restricted is not confirmed
  images unknown: 1 running digest(s) across 2 container(s); vulnerability data not configured
Revision:   4 (2026-09-26T02:21:21.027754Z)
PSS level:  at most restricted (unconfirmed: 9 checks not visible to kguardian)

DIMENSION    STATUS   COVERAGE  REASON
podSecurity  unknown  partial   Every evaluated check passes restricted, but 9 checks cannot be seen (e.g. hostPath), so restricted is not confirmed
network      unknown  full      No audit policy covers this workload; applied NetworkPolicies are not visible to the broker
syscalls     warn     partial   No SeccompProfile CR references this workload
images       unknown  partial   1 running digest(s) across 2 container(s); vulnerability data not configured
compute      ok       full      1 container(s) have no memory limit

Needs attention:
SEVERITY  FINDING                        CONTAINER  TITLE
medium    syscalls.no_enforcing_profile  -          No SeccompProfile CR enforces the observed syscall set

Readiness:
  trafficObserved24h      yes         Traffic observed for 6d 0h
  syscallCaptureComplete  no          Capture level medium on 1 pod(s)
  noWouldDeny24h          can't tell  No audit policy covers this workload
  imageSigned             can't tell  Signature verification is not configured
  podSecurityRestricted   can't tell  Every evaluated check passes restricted; 9 checks cannot be seen, so restricted is not confirmed
```

* **Posture** is `ok` only when all four core dimensions (network,
  syscalls, podSecurity, images) are known and ok. Otherwise it is the worst
  known `warn` or `risk`, and `unknown` when nothing known is worse than ok.
  **Coverage** is how many of the four kguardian knows. Compute is
  informational and outside the rollup. Each dimension that is not `ok` gets
  a one-line reason under the posture.
* **images** stays `unknown` until a vulnerability source exists. Running
  digests, mixed rollouts, crash loops and pull failures are still reported
  as inventory facts and findings; they are not a verdict on the image.
* **PSS level** "at most restricted" means every check kguardian can evaluate
  passes, but volumes, host ports, AppArmor, SELinux, procMount and sysctls
  are not reported to kguardian and could still fail. That is why
  podSecurity is `unknown` there rather than `ok`, and why
  `podSecurityRestricted` prints `can't tell`. A `privileged` level is
  `risk`; `baseline` is at least `warn`.
* A container that was renamed or removed from the spec prints as a
  **Stale** line and is left out of the level, the findings and the patch.
* When the broker has a patch to recommend, the output ends with the
  `profile export` command that prints it.

`-o json` and `-o yaml` print the broker's response unchanged, including
every finding, per-container securityContext, network peer and the
recommendation.

## profile list

Calls `GET /workloads`. Rows come from the broker's snapshot read model,
refreshed every few minutes.

| Flag              | Type   | Description                                                 | Default        |
| ----------------- | ------ | ----------------------------------------------------------- | -------------- |
| `-n, --namespace` | string | Only this namespace                                         | all namespaces |
| `--kind`          | string | Only this workload kind (case-sensitive)                    | all            |
| `--status`        | string | Only this posture status: `ok`, `warn`, `risk` or `unknown` | all            |
| `--limit`         | int    | Page size; the broker caps it at 500                        | `100`          |
| `--after`         | string | Cursor for the next page                                    | first page     |
| `-o, --output`    | string | `table`, `json` or `yaml`                                   | `table`        |

```
NAMESPACE  KIND        NAME      STATUS  COVERAGE  UNKNOWN                     FINDINGS (C/H/M)  REV
payments   Deployment  checkout  warn    25%       network,podSecurity,images  0/0/1             4
payments   Deployment  ledger    warn    25%       network,podSecurity,images  0/0/2             1
payments   Deployment  refunds   warn    25%       network,syscalls,images     0/0/2             1
```

`UNKNOWN` lists the core dimensions kguardian has no data for; `images` is always among them until vulnerability data exists.
`--status unknown` lists the workloads it cannot judge at all. When more
pages exist, the `--after` cursor is printed on stderr.

## profile diff

Calls `GET /workloads/{namespace}/{kind}/{name}/profile/diff`. The broker
stores a new revision whenever the observed, policy-relevant behaviour
changes: securityContext, running digests, the syscall set, or the set of
network rules. A revision records what kguardian observed, not what is
applied in the cluster.

| Flag           | Type   | Description               | Default                    |
| -------------- | ------ | ------------------------- | -------------------------- |
| `--from`       | int    | Older revision            | the revision before `--to` |
| `--to`         | int    | Newer revision            | the latest                 |
| `-o, --output` | string | `table`, `json` or `yaml` | `table`                    |

```
Workload: payments/Deployment/checkout
From:     2 (2026-09-26T02:05:41.141561Z)
To:       4 (2026-09-26T02:07:55.676169Z)

podSecurity: changed
  containers:
    migrate
      fields:
        securityContext.allowPrivilegeEscalation: unset -> false
        securityContext.capabilitiesDrop: unset -> ["ALL"]
  level: baseline -> restricted

network: changed
  added:
    + egress TCP/443 external:198.51.100.20

syscalls: unchanged

images: unchanged
```

`unset` means a spec field was not set at that revision. `unknown` means a
derived value (the PSS level, the capture level) was not known at that
revision. If retention has trimmed the revision before `--to`, the broker
compares with the newest retained one and the output says so in a `Note:`
line. The latest revision number is shown by `profile get`.

## profile export

Prints a recommendation derived from the profile. kguardian never applies
it.

| Flag       | Type   | Description                                                         | Default |
| ---------- | ------ | ------------------------------------------------------------------- | ------- |
| `--format` | string | `pss`: the recommended `securityContext` as a strategic-merge patch | `pss`   |

The patch sets only the fields that fail an evaluated Pod Security Standards
check, under the right path for the kind (`spec.template.spec` for a
Deployment or DaemonSet, `spec.jobTemplate.spec.template.spec` for a
CronJob). stdout is the patch and nothing else; caveats go to stderr:

```bash theme={null}
kubectl kguardian profile export observability/DaemonSet/node-exporter --format pss > node-exporter-pss.yaml
```

```yaml theme={null}
# kguardian recommendation, not applied. Review before use.
# Target: Pod Security Standards restricted (kubernetes.io/docs/concepts/security/pod-security-standards)
spec:
  template:
    spec:
      hostNetwork: false
      hostPID: false
      securityContext:
        seccompProfile:
          type: RuntimeDefault
      containers:
      - name: node-exporter
        securityContext:
          allowPrivilegeEscalation: false
          capabilities:
            drop: ["ALL"]
```

Read the caveats before applying anything. For this node agent they say
that turning off `hostNetwork` and `hostPID` breaks components that need the
node's namespaces, and that a namespace-level PSS exemption is often the
right answer instead. If you still want the patch, apply it yourself:

```bash theme={null}
kubectl patch daemonset node-exporter -n observability --patch-file node-exporter-pss.yaml
```

When the broker has no patch to recommend, the command prints nothing on
stdout and explains why on stderr. That is not a pass: it happens when every
check kguardian can see passes, which is only an upper bound, or when the
only failing container is an ephemeral one that a patch cannot change. If no
container `securityContext` has been reported for the workload, the command
exits non-zero: there is nothing to base a recommendation on.

Global flags (`--kubeconfig`, `--context`, `--broker-namespace`,
`--broker-service`, `--broker-token-file`, `--debug`) are listed in the
[CLI overview](/cli/overview). A `401` or `403` from the broker prints a hint:
these commands need a token with the `read` scope.
