## Why
Deploy WatchState (arabcoders/watchstate), the Jellyfin/Plex/Emby watch-state
sync tool, as an internal admin tool. It gets an admin-only web UI/API gated the
same way as logviewer: an oauth2-proxy front backed by Authentik OIDC.
## What
- New `watchstate` namespace + media-project app; base at `apps/base/watchstate`,
overlay at `apps/overlays/au-syd1/watchstate`.
- Image `ghcr.io/arabcoders/watchstate:v1.10.3` (current release; canonical
upstream name, containerd mirrors route ghcr via artifactapi).
- `replicas: 1`, `strategy: Recreate`, single `5Gi` `cephrbd-fast-delete` RWO PVC
at `/config` — sqlite + the in-container cron/redis are single-writer.
- `runAsUser/runAsGroup/fsGroup: 1000` (image's rootless user); liveness/readiness
`GET /v1/api/system/healthcheck` on 8080 (route confirmed in upstream
`src/API/System/HealthCheck.php`, no auth guard).
- oauth2-proxy (mirrors logviewer 1:1) fronts every path. Authentik issuer
`identity.k8s.syd1.au.unkin.net`, redirect
`https://watchstate.k8s.syd1.au.unkin.net/oauth2/callback`. Authorization is
enforced Authentik-side (akR-global-admin only), so no oauth2-proxy group
allowlist is configured.
- Internal-only Gateway (`traefik-internal`) for
`watchstate.k8s.syd1.au.unkin.net`, `vault-issuer` TLS leaf, external-dns to
`198.18.200.4`. HTTP -> HTTPS redirect.
- `VaultStaticSecret` pulls OIDC creds from
`kv/kubernetes/namespace/watchstate/default/oauth-credentials`; `vault-ca-cert`
auto-reflects into the namespace.
- Registered in the media `ApplicationSet` + `AppProject`.
## Scrape decision
No `VMPodScrape`: WatchState exposes no Prometheus/`/metrics` endpoint.
## Follow-ups
- **Seed check:** the VaultStaticSecret expects `client_id`, `client_secret`, and
`cookie_secret` keys at the kv path. Only `client_secret` was confirmed seeded;
`client_id` and a generated `cookie_secret` must also be present or the
oauth2-proxy pod will not start.
- **Webhook ingestion:** the Jellyfin webhook endpoint (`/v1/api/webhook`)
currently sits behind oauth2-proxy like everything else. When sync is wired up,
the jellyfins pushing webhooks will need an auth-bypass or an apikey route for
that path.
Reviewed-on: #419
Co-authored-by: unkin-agent <unkin-agent@unkin.net>
Co-committed-by: unkin-agent <unkin-agent@unkin.net>