# SessionRetentionPolicy CRD

`SessionRetentionPolicy` defines how long session history data is kept across the storage tiers the session-api manages: the Postgres **warm store** and an optional **cold archive** (S3, GCS or Azure). The policy carries no binding information; the compaction job selects which policy it applies.

## API Version

```yaml
apiVersion: omnia.altairalabs.ai/v1alpha1
kind: SessionRetentionPolicy
```

**Short name:** `srp` (e.g. `kubectl get srp`).

## Resource Scope

`SessionRetentionPolicy` is **cluster-scoped**.

## How Policies Are Applied

The operator writes each policy's spec to the ConfigMap
`retention-policy-<policy-name>` (key `retention.yaml`) in the operator's
namespace. See [Session retention](/explanation/agents/session-retention/) for
how the compaction CronJob applies it.

## Spec Fields

Both tier blocks and `tagOverrides` are optional.

### `warmStore`

Configuration for the Postgres warm store tier.

| Field | Type | Default | Notes |
|---|---|---|---|
| `warmStore.retentionDays` | int32 | `7` | Days to retain data in the warm store. Range `1`–`3650`. |

### `coldArchive`

Configuration for the cold archive tier (S3, GCS or Azure).

| Field | Type | Default | Notes |
|---|---|---|---|
| `coldArchive.enabled` | bool | `false` | Whether expired warm-store sessions are archived rather than deleted. |
| `coldArchive.retentionDays` | int32 | — | Days to retain data in the cold archive. Range `1`–`36500`. **Required when `enabled` is `true`** (enforced by CEL validation). |

### `tagOverrides`

Gives the sessions that carry a tag their own warm-store lifetime, in place of
`warmStore.retentionDays`. Dev (try-it) sessions from the arena builder carry
`kind:dev`, so an override on that tag expires try-it traffic sooner than
production.

| Field | Type | Default | Notes |
|---|---|---|---|
| `tagOverrides[].tag` | string | — | **Required.** The session tag, exactly as recorded, e.g. `kind:dev`. 1–256 characters, unique within the list. |
| `tagOverrides[].warmStore.retentionDays` | int32 | `7` | Days to retain the tagged sessions in the warm store. Range `1`–`3650`. Can be shorter or longer than `warmStore.retentionDays`. |

At most 16 overrides. A session that matches several overrides takes the first
in the list. The default retention never applies to a session that matches an
override, so an override can lengthen a tag's lifetime as well as shorten it.
An override is global: it applies to the tagged sessions of every workspace the
compaction job scans, and it is not scoped to a workspace. A per-workspace
warm-store retention in the compaction job's config file (a `perWorkspace`
entry, which only a hand-written config carries) does not apply to a tagged
session.
Cold archive retention has no override: archived sessions expire on
`coldArchive.retentionDays` whatever their tags.

## Status Fields

| Field | Type | Description |
|---|---|---|
| `status.phase` | string | `Active` (spec valid and ConfigMap synced) or `Error` (validation or ConfigMap sync failed). |
| `status.observedGeneration` | int64 | Most recent generation observed by the controller. |
| `status.conditions` | Condition[] | See below. |

The controller sets `status.phase` as follows.

```mermaid
stateDiagram-v2
    [*] --> Active: spec valid and ConfigMap synced
    [*] --> Error: validation fails (Ready=False, ValidationFailed)
    [*] --> Error: ConfigMap sync fails (Ready=False, ConfigSyncFailed)
    Error --> Active: next reconcile succeeds
    Active --> Error: spec or sync becomes invalid
```

### Conditions

| Type | Meaning |
|---|---|
| `Ready` | `True` (reason `AllChecksPass`) when the spec is valid and the ConfigMap is synced; `False` with `ValidationFailed` or `ConfigSyncFailed` otherwise. |
| `PolicyValid` | Result of spec validation. |
| `WorkspacesResolved` | Always `True` (reason `NotApplicable`). |
| `Enforced` | Whether anything applies the policy: `True` (`CompactionJobPresent`) when a CronJob labelled `app.kubernetes.io/component: compaction` exists in the operator namespace, `False` (`NoCompactionJob`) when none does, `Unknown` (`LookupFailed`) when the lookup fails. `Ready` and `phase` describe the policy itself and do not reflect this. |

## Print Columns

`kubectl get sessionretentionpolicy` shows:

| Column | Source |
|---|---|
| `Ready` | `.status.conditions[?(@.type=='Ready')].status` |
| `Phase` | `.status.phase` |
| `Warm Days` | `.spec.warmStore.retentionDays` |
| `Cold Archive` | `.spec.coldArchive.enabled` |
| `Age` | `.metadata.creationTimestamp` |

## Examples

### Minimal policy — warm store retention only

```yaml
apiVersion: omnia.altairalabs.ai/v1alpha1
kind: SessionRetentionPolicy
metadata:
  name: short-retention
spec:
  warmStore:
    retentionDays: 14
```

### Full policy — warm store and cold archive

```yaml
apiVersion: omnia.altairalabs.ai/v1alpha1
kind: SessionRetentionPolicy
metadata:
  name: standard-retention
spec:
  warmStore:
    retentionDays: 90
  coldArchive:
    enabled: true
    retentionDays: 365
```

### Shorter retention for dev sessions

```yaml
apiVersion: omnia.altairalabs.ai/v1alpha1
kind: SessionRetentionPolicy
metadata:
  name: default
spec:
  warmStore:
    retentionDays: 30
  tagOverrides:
    - tag: kind:dev
      warmStore:
        retentionDays: 2
```

## Related Resources

- [Workspace CRD](/reference/core/workspace/) — service groups and the session-api
- [SessionPrivacyPolicy CRD](/reference/policies/sessionprivacypolicy/) — per-session privacy rules
- [Session retention](/explanation/agents/session-retention/) — how the compaction CronJob applies a policy
- [Configure Sessions](/how-to/agents/configure-sessions/) — session service setup