---
path: /studio/aja/content-watermarking
surface: studio
domain: aja
auth: signed-in + studio entitlement
source: apps/oshun/web/src/app/studio/aja/content-watermarking/page.tsx
status: walked
last_walked:
  '2026-05-29 automated runtime walk (Playwright headless) — render, /v1 data
  (2xx), console/page-errors, expected content, screenshot verified; live
  screen-reader, touch, offline, and telemetry-delivery checks pending a manual
  AT pass. Evidence: WALKTHROUGH/results/runtime-sweep-2026-05-29.md; body
  re-derived 2026-06-03 from current source (lane-console architecture)'
---

# Studio Aja · Content Watermarking

## Purpose

Admin lane console for the real `@aja/content-watermarking` MotionWatermarker
payload codec. The operator fills a provenance / leak-tracing payload
(watermark id, content id, owner id, ISO-8601 timestamp, optional custom JSON)
and encodes it into watermark bytes plus an integrity checksum; a second action
decodes those bytes back to the original payload to confirm round-trip detection.

## Entry points

- **Direct URL / bookmark** — `/studio/aja/content-watermarking`; metadata
  canonical to this path
- **Studio nav** — Domain-bridge studios section of `/studio`
- **Breadcrumb parent** — `/studio/aja/content-security` (declared in the
  page.tsx breadcrumb `<ol>`)
- **Sibling quick-actions on this page**: Content Security, Content Moderation,
  Privacy Protection, Data Retention, Back to Studio
- **Operator surfacing** — see
  [`../../studio-overview.md`](../../studio-overview.md)

## Layout regions

`page.tsx` renders inside `<ShellLayout active="studio">`:

- **Breadcrumb panel** (`data-aja-content-watermarking-breadcrumbs`):
  `<nav aria-label="Aja Content Watermarking breadcrumbs">` →
  `Studio / Aja Content Security / Content Watermarking`
- **Workspace** (`<StudioAjaContentWatermarkingWorkspace />`, archetype: LEAF):
  - `<h1>` (`WorkspaceHeading`) "Aja Content Watermarking Workspace"
  - Summary `<p data-aja-cw-summary>` — encode a payload into watermark bytes
    (with checksum) and decode bytes back
  - The encode form lane + the encoded/decoded result blocks
- **Route Map panel** (`data-aja-content-watermarking-route-map`): `<h2>Route
  Map</h2>` listing 6 entries from `STUDIO_AJA_CONTENT_WATERMARKING_ROUTE_MAP`
  (primary, rules, traces, packages, parity, governance)
- **Quick-action panel**: five `Link.quickAction` anchors — Content Security,
  Content Moderation, Privacy Protection, Data Retention, Back to Studio

## States

- [ ] **Loading** — GETs the codec catalog on mount; renders
      `<p data-aja-cw-loading>` "Loading watermark codec…"
- [ ] **Unauthorized** — 401/403 renders `data-aja-cw-unauthorized` ("Access
      restricted." + admin-scope message, default "Aja workspace scope required
      to view content watermarking.")
- [ ] **Error** — non-OK / malformed / unreachable renders `data-aja-cw-error`
      ("Could not load content watermarking." + reason)
- [ ] **Ready (form)** — success renders the `data-aja-cw-encode-form`
- [ ] **Encoded** — a successful encode renders `data-aja-cw-encoded` with
      `data-aja-cw-checksum`, `data-aja-cw-bytelength`, and a
      `data-aja-cw-decode-submit` button
- [ ] **Decoded** — a successful decode renders `data-aja-cw-decoded` with
      `data-aja-cw-detected` and (if present) `data-aja-cw-recovered`
      (recovered owner / content)
- [ ] **Action error** — invalid custom JSON, missing required fields, decode
      without an encode, or POST failure renders `data-aja-cw-action-error`
- [x] **Empty / Standalone PWA / offline** — N/A beyond the above; shell SSR,
      lane is a client fetch on mount

## Interactions

### Encode lane (`data-aja-cw-encode-form`)

- [ ] **Watermark id** (`data-aja-cw-field-id`, input, label "Watermark id")
- [ ] **Content id** (`data-aja-cw-field-content`, input, label "Content id")
- [ ] **Owner id** (`data-aja-cw-field-owner`, input, label "Owner id")
- [ ] **Timestamp** (`data-aja-cw-field-timestamp`, input, label "Timestamp
      (ISO-8601)")
- [ ] **Custom data** (`data-aja-cw-field-custom`, input, label "Custom data
      (JSON, optional)")
- [ ] **"Encode watermark"** (`data-aja-cw-encode-submit`, submit) — fires
      `POST /v1/admin/aja/content-watermarking/encode` with `{ id, contentId,
      ownerId, timestamp, customData? }`
- [ ] **"Decode it back"** (`data-aja-cw-decode-submit`, button) — fires
      `POST /v1/admin/aja/content-watermarking/decode` with the encoded
      `{ dataBase64 }`

### Route Map panel

- [ ] **Route entries** (6 × `<article>`) — non-interactive path+purpose

### Quick-action panel

- [ ] **"Open Aja Content Security workspace"** → `/studio/aja/content-security`
- [ ] **"Open Aja Content Moderation workspace"** →
      `/studio/aja/content-moderation`
- [ ] **"Open Aja Privacy Protection workspace"** →
      `/studio/aja/privacy-protection`
- [ ] **"Open Aja Data Retention workspace"** → `/studio/aja/data-retention`
- [ ] **"Back to Studio workspace index"** → `/studio`

## Data & contracts

- **Reads**: `GET /v1/admin/aja/content-watermarking` on mount — catalog
  `{ payloadFields: string[], codec }`
- **Writes**:
  - `POST /v1/admin/aja/content-watermarking/encode` → `{ encoded }`
    (dataBase64, byteLength, checksum)
  - `POST /v1/admin/aja/content-watermarking/decode` → `{ detected, payload }`
- **Realtime**: none
- **Caching**: client `fetch` on mount with `cache: 'no-store'` and
  `buildBffAuthHeaders()`; SSR shell only
- **Auth/role check**: BFF endpoints admin-scoped + fail-closed (401/403 →
  unauthorized render); route behind the signed-in + studio proxy gate

## Cross-references

- Parent: [`../../studio-overview.md`](../../studio-overview.md)
- Shell:
  [`../../../shell/04-auth-session.md`](../../../shell/04-auth-session.md)
- Sibling routes:
  [`./studio-aja-content-security.md`](./studio-aja-content-security.md)
  (breadcrumb parent),
  [`./studio-aja-content-moderation.md`](./studio-aja-content-moderation.md),
  [`./studio-aja-privacy-protection.md`](./studio-aja-privacy-protection.md),
  [`./studio-aja-data-retention.md`](./studio-aja-data-retention.md)
- Component sources:
  - `apps/oshun/web/src/components/studio/StudioAjaContentWatermarkingWorkspace.tsx`
  - `apps/oshun/web/src/components/studio/StudioAjaContentWatermarkingRouteMap.ts`

## Open questions / known gaps

- [ ] The decode action requires an in-session encode first (it reuses the
      encoded `dataBase64`); there is no standalone "paste bytes to decode" path
- [ ] Route-map sub-routes are advertised but have no `page.tsx` in V1
