---
path: /studio/aja/data-retention
surface: studio
domain: aja
auth: signed-in + studio entitlement
source: apps/oshun/web/src/app/studio/aja/data-retention/page.tsx
status: walked
last_walked:
  '2026-07-26 authority-boundary runtime coverage — unbound deploy authority,
  disabled mutations, responsive layout, and automated WCAG 2.1 AA scan'
---

# Aja Data Retention

## Purpose

Admin lane console for data-retention governance. The taxonomy catalog is always
available, but policies and evaluations are authoritative only when the BFF is
explicitly bound to an owning deploy service used by storage lifecycle and
deletion executors. The default deployment is currently unbound: it reports
`aja_data_retention_authority_not_configured`, disables both forms, and returns
503 for direct mutation/evaluation requests. `AjaDataRetentionStore` is retained
only as an explicitly injected process-local test/development adapter.

## Entry points

- **Direct URL** — `/studio/aja/data-retention` (signed-in + studio entitlement)
- **Breadcrumb** — Studio / Aja Content Security / Data Retention
- **Sibling quick-actions from this page** — Content Security, Content
  Moderation, Content Watermarking, Privacy Protection, Back to Studio

## Layout regions

`page.tsx` mounts `ShellLayout` with `active="studio"` and renders four panels.

- **Breadcrumb panel** (`data-aja-data-retention-breadcrumbs`):
  `nav aria-label="Aja Data Retention breadcrumbs"` — Studio (link) / Aja
  Content Security (link) / Data Retention (`aria-current="page"`)
- **Workspace panel** (`data-aja-data-retention-workspace`):
  - `<h1>` "Aja Data Retention Workspace" (via `WorkspaceHeading`)
  - `<p data-aja-data-retention-summary>` — inspect the retention taxonomy and,
    when its owning authority is connected, create policies or evaluate records
  - The retention-policies section (`<h2>` "Retention policies (N)" + list +
    create form) and the "Evaluate a record" section (`<h2>` + evaluate form)
- **Route Map panel** (`data-aja-data-retention-route-map`): `<h2>` "Route
  Map" + one `<article>` per `STUDIO_AJA_DATA_RETENTION_ROUTE_MAP` entry (6
  entries)
- **Quick actions panel**: `.quickAction` `<Link>`s — see Interactions

## States

- [ ] **Loading** — `data-aja-dr-loading` "Loading retention policies…"
- [ ] **Unauthorized** — on 401/403, `data-aja-dr-unauthorized` ("Access
      restricted." + reason, default "Aja workspace scope required to view data
      retention.")
- [ ] **Error** — non-OK / malformed / unreachable → `data-aja-dr-error`
- [ ] **Ready (forms)** — catalog present: policy list (`data-aja-dr-policy` per
      category), create-policy form, evaluate form
- [x] **Authority unavailable** — `data-aja-authority-status="data-retention"`
      names the boundary; both forms are disabled and emit no request
- [ ] **Result** — `data-aja-dr-evaluation` (with `data-aja-dr-expired`
      attribute) shows `data-aja-dr-applicable` (applicable policy name or
      "none"), expires, expired

## Interactions

### Create-policy form (`data-aja-dr-policy-form`)

This form is enabled only when `authority.configured` is true.

- [ ] **Name** — `input data-aja-dr-field-name`
- [ ] **Category** — `select data-aja-dr-field-category` (from
      `catalog.categories`)
- [ ] **Retention value** — `input data-aja-dr-field-value` (number)
- [ ] **Unit** — `select data-aja-dr-field-unit` (from `catalog.units`)
- [ ] **Basis** — `select data-aja-dr-field-basis` (from `catalog.bases`)
- [ ] **Create policy** — `button data-aja-dr-policy-submit`;
      `POST     /v1/admin/aja/data-retention/policies` with
      `{ name, category, retention:     { value, unit }, retentionBasis, priority }`,
      then reloads the catalog

### Evaluate form (`data-aja-dr-evaluate-form`)

This form is enabled only when `authority.configured` is true.

- [ ] **Category** — `select data-aja-dr-eval-category` (from
      `catalog.categories`)
- [ ] **Created-at ISO** — `input data-aja-dr-eval-created`
- [ ] **Legal hold** — `input data-aja-dr-eval-legal-hold` (checkbox)
- [ ] **Evaluate** — `button data-aja-dr-evaluate-submit`;
      `POST     /v1/admin/aja/data-retention/evaluate` with
      `{ category, createdAtIso,     hasLegalHold }`

### Route Map panel (`data-aja-data-retention-route-map`)

Non-interactive `<article>` blocks from `STUDIO_AJA_DATA_RETENTION_ROUTE_MAP` (6
entries): `/studio/aja/data-retention` plus `…/scenes/[sceneId]`,
`…/exports/[exportId]`, `…/revisions/[revisionId]`, `…/parity/[parityId]`,
`…/governance/[policyId]`.

### Quick actions panel

- [ ] **Open Aja Content Security workspace** → `/studio/aja/content-security`
- [ ] **Open Aja Content Moderation workspace** →
      `/studio/aja/content-moderation`
- [ ] **Open Aja Content Watermarking workspace** →
      `/studio/aja/content-watermarking`
- [ ] **Open Aja Privacy Protection workspace** →
      `/studio/aja/privacy-protection`
- [ ] **Back to Studio workspace index** → `/studio`

## Data & contracts

- **Reads**: `GET /v1/admin/aja/data-retention` (catalog: `authority`,
  `policies`, `categories`, `units`, `bases`) on mount via
  `buildBffAuthHeaders()`, `cache: 'no-store'`
- **Writes**: `POST …/policies` (creates a policy, then re-reads the catalog);
  `POST …/evaluate` → `{ applicablePolicy, expired, expiresAtIso }`; valid
  requests return 503 while the deploy authority is unbound
- **Realtime**: none
- **Caching**: client fetch on mount (no-store); SSR shell only
- **Auth**: BFF endpoints admin-scoped + fail-closed (401/403 → unauthorized
  render); the route is signed-in + studio (proxy gate)

## Cross-references

- Parent: [`../../studio-overview.md`](../../studio-overview.md)
- Component sources:
  - `apps/oshun/web/src/components/studio/StudioAjaDataRetentionWorkspace.tsx`
  - `apps/oshun/web/src/components/studio/StudioAjaDataRetentionRouteMap.ts`
- Siblings from quick-actions: `/studio/aja/content-security`,
  `/studio/aja/content-moderation`, `/studio/aja/content-watermarking`,
  `/studio/aja/privacy-protection`

## Open questions / known gaps

- [ ] Bind an owning durable retention service that the real storage lifecycle
      and deletion executors consume, including legal-hold and subject
      deletion/export semantics, before enabling production decisions
- [ ] Route-map sub-routes
      (`scenes`/`exports`/`revisions`/`parity`/`governance`) are descriptive
      only; confirm implementation status
- [ ] Document the exact studio entitlement / proxy gate governing
      `/studio/aja/*`
