# Configure privacy policies

Create a `SessionPrivacyPolicy`, bind it to a workspace service group, override it for one agent, and rotate its encryption key.

A `SessionPrivacyPolicy` declares what to redact and how to encrypt. Per-user opt-out and consent live in privacy-api: see [Manage user consent and opt-out](/how-to/privacy/manage-user-consent/). For every field, see the [SessionPrivacyPolicy reference](/reference/policies/sessionprivacypolicy/).

## Prerequisites

- Omnia installed with `enterprise.enabled: true`, and the `omnia-crds` release installed with `enterprise.enabled=true`. A license is not required: without one Omnia runs in dev mode.
- A Workspace with at least one service group.
- `kubectl` access to the cluster.

## Create a policy

A policy must live in the Workspace's namespace, the value of `spec.namespace.name` on the Workspace.

```bash
kubectl apply -f - <<'EOF'
apiVersion: omnia.altairalabs.ai/v1alpha1
kind: SessionPrivacyPolicy
metadata:
  name: default-privacy
  namespace: my-workspace-ns
spec:
  recording:
    pii:
      redact: true
      patterns:
        - email
        - ssn
        - credit_card
      strategy: replace
EOF
```

Check that the policy is active:

```bash
kubectl get sessionprivacypolicy default-privacy -n my-workspace-ns
```

The `PHASE` column shows `Active`.

## Bind the policy to a service group

1. Edit the Workspace and add `privacyPolicyRef` to the service group:

   ```bash
   kubectl edit workspace my-workspace
   ```

   ```yaml
   spec:
     services:
       - name: default
         privacyPolicyRef:
           name: default-privacy
   ```

2. Check the `PrivacyPolicyResolved` condition:

   ```bash
   kubectl get workspace my-workspace \
     -o jsonpath='{.status.conditions[?(@.type=="PrivacyPolicyResolved")]}'
   ```

   A resolved reference reports `status: "True"` and `reason: PolicyResolved`. The message lists each binding, for example `services[default] -> default-privacy`.

## Override the policy for one agent

1. Create the stricter policy in the agent's namespace:

   ```bash
   kubectl apply -f - <<'EOF'
   apiVersion: omnia.altairalabs.ai/v1alpha1
   kind: SessionPrivacyPolicy
   metadata:
     name: strict-privacy
     namespace: my-workspace-ns
   spec:
     recording:
       pii:
         redact: true
         patterns:
           - email
           - ssn
           - credit_card
           - phone_number
           - ip_address
         strategy: hash
   EOF
   ```

2. Set `spec.privacyPolicyRef` on the AgentRuntime:

   ```bash
   kubectl patch agentruntime sensitive-agent -n my-workspace-ns \
     --type merge -p '{"spec":{"privacyPolicyRef":{"name":"strict-privacy"}}}'
   ```

3. Check the condition on the AgentRuntime:

   ```bash
   kubectl get agentruntime sensitive-agent -n my-workspace-ns \
     -o jsonpath='{.status.conditions[?(@.type=="PrivacyPolicyResolved")]}'
   ```

session-api resolves a policy in this order: the agent's `spec.privacyPolicyRef`, then its service group's, then the global default. See [resolution order](/reference/policies/sessionprivacypolicy/#resolution-order).

## Set a global default

A policy named `default` in the operator's namespace (`omnia-system` in a default install) applies to every agent that has no service group policy and no agent override. The operator installs one, in its `omnia-resources` release, when `enterprise.enabled` and `sessionPrivacy.defaultPolicy.create` are `true`, which is the default. To manage your own, apply it directly:

```bash
kubectl apply -f - <<'EOF'
apiVersion: omnia.altairalabs.ai/v1alpha1
kind: SessionPrivacyPolicy
metadata:
  name: default
  namespace: omnia-system
spec:
  recording:
    pii:
      redact: true
      patterns:
        - email
        - phone_number
EOF
```

## Rotate encryption keys

Rotation asks the KMS for a new version of the key already named in `encryption.keyID`. The policy needs `encryption.enabled: true` and `encryption.keyRotation.enabled: true`.

1. Choose how to rotate:
   - On a schedule: set `encryption.keyRotation.schedule` to a cron expression such as `"0 0 1 * *"`.
   - Immediately: annotate the policy.

     ```bash
     kubectl annotate sessionprivacypolicy my-policy -n my-workspace-ns \
       omnia.altairalabs.ai/rotate-key=true
     ```

2. To rewrite existing records onto the new version, set `encryption.keyRotation.reEncryptExisting: true`. The pass covers message `content`, tool call `arguments`, `result` and `errorMessage`, and runtime event `data` and `errorMessage`, and leaves plaintext records unchanged.

3. To re-encrypt, give the arena-controller, which runs the key-rotation controller, the session database DSN. Add the flag to the chart values and upgrade the release:

   ```yaml
   enterprise:
     arena:
       controller:
         extraArgs:
           - --session-postgres-conn=<session-database-dsn>
   ```

   Without the flag, rotation still creates the new key version and skips re-encryption.

`encryption.keyID` and `encryption.kmsProvider` are immutable once set, and the `encryption` block cannot be removed. To move to a different key or KMS, let the old records age out under retention or erase them, then create a new policy. Keep the old key in the KMS while any record it sealed is within retention.

Setting `encryption.enabled: false` stops encrypting new writes. Records already encrypted are still decrypted with the policy's key.

## Verify

Check the rotation status:

```bash
kubectl get sessionprivacypolicy my-policy -n my-workspace-ns \
  -o jsonpath='{.status.keyRotation}'
```

`currentKeyVersion` changes after a rotation. With `reEncryptExisting` set, `reEncryptionProgress.status` moves from `InProgress` to `Completed` or `Failed`.

## Troubleshooting

### `PrivacyPolicyResolved` is false with reason `PolicyNotFound`

The policy named in `privacyPolicyRef.name` does not exist in the expected namespace. Check the spelling, then list the policies in the workspace's namespace:

```bash
kubectl get sessionprivacypolicy -n my-workspace-ns
```

### A change does not take effect

session-api and memory-api pick up changes to policies and AgentRuntimes in their own namespace as the API server reports them. They re-read the global default policy and the Workspace service-group binding every 30 seconds, so a change to either takes up to 30 seconds.

### Find which policy applies to an agent

No endpoint reports the effective policy. Read the agent's override, then its service group's binding, then the global default:

```bash
kubectl get agentruntime my-agent -n my-workspace-ns \
  -o jsonpath='{.spec.privacyPolicyRef.name}'
```

## Related pages

- [SessionPrivacyPolicy reference](/reference/policies/sessionprivacypolicy/): fields, encryption coverage per object type and example YAML.
- [Manage user consent and opt-out](/how-to/privacy/manage-user-consent/): record and read per-user preferences via privacy-api.
- [Handle data subject erasure (DSAR)](/how-to/privacy/handle-data-subject-erasure/): erase all of a user's data via privacy-api.