---
name: convention-mux
type: convention
description: Conventions for Mux video in Aled's system — the per-project environment model, the setup sequence for standing up a dedicated Mux environment and least-privilege credentials, the Vercel/Sanity wiring, playback defaults, and what requires human action. Read before any operation that stands up or touches Mux video for a project, so the sequence is followed rather than re-derived each time. Sits beside convention-vercel and convention-sanity; grown as the conventions solidify.
---

# convention-mux

How Mux is used for video in Aled's system. Stub — grown as the conventions solidify. Companion to `convention-vercel` (deployment) and `convention-sanity` (content), for the projects that carry video in a work/portfolio block. Standing up a Mux environment has been done operator-directed, step by step, for each project (meirionpritchard-com, careerOS) — this convention fixes the sequence so the next one is followed, not re-derived.

## Role

Mux is the video host and player for projects whose content model carries video (the work / portfolio block). The CMS holds the Mux asset reference; the front end renders the player from it. Mux has two products behind one dashboard — **Mux Video** (encoding, hosting, playback; this is what we use) and **Mux Data** (viewer analytics) — and the setup traps below turn on not confusing them.

## Projects in scope

| Project | Repo | Mux environment |
| -- | -- | -- |
| meirionpritchard-com | assoc-one/meirionpritchard-com | `meirionpritchard-com` |
| careerOS | (cOS.Build) | per COS-222 |

One Mux **environment per project** — this isolates assets, usage, and billing per project, the same per-project split as Vercel and Sanity.

## Setup — standing up a per-project Mux environment

New environment provisioning is **human** (the `agent`-group value) — it needs account-level Mux dashboard access and generates a secret shown once. The operator does it; agents reference the environment name and the Vercel-set credentials once they exist. The sequence:

1. **Create the environment.** Mux dashboard → environment switcher → **Create environment**, name it `<project>` (matching the repo/Vercel project), Member Access: All Members. One environment per project.
2. **Dismiss the "Get started with Data" onboarding.** Mux drops you into Mux **Data** (viewer analytics) first; its "Environment key" is **not** the Video credential. This is the common wrong turn — the Data env key does not authenticate Video API calls, so grabbing it here leads to a silent dead end. Skip past Data.
3. **Generate a Video API access token.** Settings → **API Access Tokens** → **Generate**, with the correct environment selected, scoped to **Mux Video → Read + Write only** (least-privilege — no Data, no System scopes). This yields `MUX_TOKEN_ID` and `MUX_TOKEN_SECRET`; the secret is shown once, so capture it immediately.
4. **Wire the credentials into Vercel.** Add both `MUX_TOKEN_ID` and `MUX_TOKEN_SECRET` to the Vercel project (Production **and** Preview scopes), then redeploy. The embedded `/studio` (Sanity Studio) rides the same Vercel deploy, so no separate Sanity Studio environment variable is needed — the studio reads the same Vercel-injected values (see `convention-vercel` for env-var scopes, `convention-sanity` for the embedded-studio setup).
5. **Default to public playback** unless there is a specific reason to restrict. A signing key for signed/private playback can be added later without redoing the environment, so start public and tighten only if the content requires it.

## Environment variables

- `MUX_TOKEN_ID` / `MUX_TOKEN_SECRET` — the Video API token pair, least-privilege (Read + Write on Mux Video only). Secrets live in Vercel, never in the repo (`convention-vercel`).
- Set on both **Production** and **Preview** scopes so preview deploys can also upload and play.

## Gotchas

- **Mux Data vs Mux Video.** The dashboard opens on Data; its "Environment key" is analytics, not a Video credential. Video calls need an **API Access Token** (id + secret) from Settings, scoped to Mux Video. Confusing the two is the standard setup wrong turn (step 2 above).
- **The secret is shown once.** `MUX_TOKEN_SECRET` is displayed at generation only. Capture it into Vercel immediately; if lost, generate a fresh token rather than hunting for it.
- **One environment per project.** Do not reuse one environment's token across projects — it defeats the per-project isolation of assets, usage, and billing.
- **Public by default is deliberate.** Signed playback adds a signing key and per-URL tokens; only take it on where the content genuinely needs restricting, since it can be added later without re-provisioning.

## Source

meirionpritchard-com Mux setup (A1-106, 2026-07-01); reference careerOS COS-222 step 1 (polymorphic media field on the slide schema). Captured via APP-405.
