Adds a bgutil PO token server next to Music Assistant so MA's YouTube Music provider can stream tracks with no local copy (MA prefers the local file when it has one).
kubernetes/apps/music-assistant/po-token-server.yaml: Deployment (non-root uid 1000, drop ALL, RuntimeDefault seccomp), ClusterIP Service on :4416, pod-scoped CNP: ingress only from the MA pod on 4416 plus kubelet probes; egress to DNS and world:443 only (the server is unauthenticated). Egress uses world:443 rather than toFQDNs because the full set of Google endpoints BotGuard needs isn't documented.
Image automation bgutil-pot-provider: bare-semver tags (node flavor), range 2.x. MA 2.10.7 pip installs bgutil-ytdlp-pot-provider unpinned at runtime (2.0.2 on PyPI today), so the server has to stay on the same major. The MA docs' "1.2.1" predates the 2.x plugin line.
Manual step after merge (can't be automated): MA UI → Settings → Providers → add YouTube Music with a Premium account cookie and PO token server URL http://po-token-server.music-assistant.svc.cluster.local:4416. The cookie expires periodically and must then be re-entered.
Verified: kubectl kustomize renders both apps/music-assistant and infrastructure/image-automation on current main.
Adds a bgutil PO token server next to Music Assistant so MA's YouTube Music provider can stream tracks with no local copy (MA prefers the local file when it has one).
- `kubernetes/apps/music-assistant/po-token-server.yaml`: Deployment (non-root uid 1000, drop ALL, RuntimeDefault seccomp), ClusterIP Service on :4416, pod-scoped CNP: ingress only from the MA pod on 4416 plus kubelet probes; egress to DNS and world:443 only (the server is unauthenticated). Egress uses world:443 rather than toFQDNs because the full set of Google endpoints BotGuard needs isn't documented.
- Image automation `bgutil-pot-provider`: bare-semver tags (node flavor), range `2.x`. MA 2.10.7 `pip install`s `bgutil-ytdlp-pot-provider` unpinned at runtime (2.0.2 on PyPI today), so the server has to stay on the same major. The MA docs' "1.2.1" predates the 2.x plugin line.
**Manual step after merge (can't be automated):** MA UI → Settings → Providers → add YouTube Music with a Premium account cookie and PO token server URL `http://po-token-server.music-assistant.svc.cluster.local:4416`. The cookie expires periodically and must then be re-entered.
Verified: `kubectl kustomize` renders both `apps/music-assistant` and `infrastructure/image-automation` on current main.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
MA's YouTube Music provider needs a bgutil PO token server; MA installs
the matching yt-dlp plugin unpinned from PyPI (2.0.2 today), so the
server tracks 2.x via image automation. The MA docs' "1.2.1" predates
the 2.x plugin line.
Reachable only from the MA pod; egress limited to DNS and world:443.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
🤖Automated review — model claude-opus-5-5, commit 96ea51014393696fad95728e9efb596d380778a0.
Automated review by the review-bot: it approves only when it found nothing red or yellow, and a human still merges. Push new commits to get a fresh review.
Summary
This PR adds a bgutil PO token server (Deployment, ClusterIP Service on :4416 and a pod-scoped CiliumNetworkPolicy) to the music-assistant namespace so Music Assistant's YouTube Music provider can stream. It also enrols the server's image in Flux image automation (2.x, bare-semver filter). The manifests are consistent with the repo:
The CNP's ingress selector matches MA's pod label (app.kubernetes.io/name: music-assistant, deployment.yaml).
MA's default route stays on eth0 (the sbr NAD), so the ClusterIP is reachable.
No policy restricts MA's own egress (#1432 left music-assistant out of scope; there is no music-assistant-ns.yaml).
The image-automation files follow the README's process.
GitHub reports the PR as mergeable. Since the PR base cb697e2, main has only gained the CoreDNS change (#1450), which touches none of these files. No other PR or code adds a PO token server.
⚪ Unverified
Image tag and flavour. I could not check that docker.io/brainicism/bgutil-ytdlp-pot-provider:2.0.2 exists, or that bare-semver tags are the node build. Both claims rest only on the PR body. The music-assistant Flux Kustomization runs with wait: true and timeout: 30m (kubernetes/clusters/homelab/music-assistant.yaml). A bad tag would leave that Kustomization not-Ready, although MA itself keeps running.
Non-root runtime and health endpoint.po-token-server.yaml:24-29 forces uid/gid 1000 and drops ALL capabilities, and the probes hit GET /ping (po-token-server.yaml:38-51). I could not check the upstream image's user, whether its working directory is writable by uid 1000, or that /ping exists. If any of these is wrong, the pod crash-loops or never becomes Ready, with the same Kustomization effect as above.
Plugin version. The claim that MA 2.10.x pip installs bgutil-ytdlp-pot-provider unpinned at runtime decides whether 2.x is the right range. It is external behaviour I could not check. MA on main is now 2.10.8 (deployment.yaml), not 2.10.7 as the PR body says.
🟡 Suggestions
CNP location.kubernetes/apps/music-assistant/po-token-server.yaml:73-114 puts the CiliumNetworkPolicy in the app directory. Every other hand-written CNP lives in kubernetes/infrastructure/network-policies/<ns>-ns.yaml and is listed in that directory's kustomization.yaml (navidrome, kuma-sync, review-bot, etc.). The only in-app CNPs are the ones authentik-gate-sync generates. Moving it to a new music-assistant-ns.yaml keeps policy review in one place, and the later per-workload egress audit of music-assistant that #1432 deferred will find it there.
World egress includes the LAN.po-token-server.yaml:108-114 allows toEntities: world on 443. #1432 describes world in this cluster as "internet + LAN". This third-party image is now auto-bumped across a whole major with no human in the loop, so the pod can reach every HTTPS service on the LAN (UDM, etc.). The PR explains why it didn't use toFQDNs. A middle ground would be toCIDRSet with 0.0.0.0/0 / ::/0 and except the RFC1918/ULA ranges, or FQDN patterns for *.google.com / *.youtube.com / *.googlevideo.com. Either keeps the "unknown Google endpoints" flexibility without reaching into the LAN.
🟢 Nits
The PR body says MA 2.10.7; main runs 2.10.8.
Nothing monitors the new server (no KumaMonitor, no alert). A broken token server only shows up as YouTube Music playback failures in MA.
Tests
The repo validates manifests in CI (ci-manifests-test), which was still pending on the head sha when I checked. The PR says it ran kubectl kustomize on both kustomizations, which is the expected check for a manifest-only change. There is no unit-test surface here. After merge, check:
the pod becomes Ready under uid 1000;
hubble observe --verdict DROPPED -n music-assistant shows no drops between MA and po-token-server:4416, or from the server to Google;
the ImagePolicy resolves to a bare 2.x.y tag.
Recommendation
Looks reasonable — no blocking issues; comments above are optional.
🤖 **Automated review** — model `claude-opus-5-5`, commit `96ea51014393696fad95728e9efb596d380778a0`.
> Automated review by the `review-bot`: it approves only when it found nothing red or yellow, and a human still merges. Push new commits to get a fresh review.
---
## Summary
This PR adds a bgutil PO token server (Deployment, ClusterIP Service on :4416 and a pod-scoped CiliumNetworkPolicy) to the `music-assistant` namespace so Music Assistant's YouTube Music provider can stream. It also enrols the server's image in Flux image automation (`2.x`, bare-semver filter). The manifests are consistent with the repo:
- The CNP's ingress selector matches MA's pod label (`app.kubernetes.io/name: music-assistant`, `deployment.yaml`).
- MA's default route stays on eth0 (the `sbr` NAD), so the ClusterIP is reachable.
- No policy restricts MA's own egress (#1432 left `music-assistant` out of scope; there is no `music-assistant-ns.yaml`).
- The image-automation files follow the README's process.
GitHub reports the PR as mergeable. Since the PR base `cb697e2`, main has only gained the CoreDNS change (#1450), which touches none of these files. No other PR or code adds a PO token server.
## ⚪ Unverified
- **Image tag and flavour.** I could not check that `docker.io/brainicism/bgutil-ytdlp-pot-provider:2.0.2` exists, or that bare-semver tags are the node build. Both claims rest only on the PR body. The `music-assistant` Flux Kustomization runs with `wait: true` and `timeout: 30m` (`kubernetes/clusters/homelab/music-assistant.yaml`). A bad tag would leave that Kustomization not-Ready, although MA itself keeps running.
- **Non-root runtime and health endpoint.** `po-token-server.yaml:24-29` forces uid/gid 1000 and drops ALL capabilities, and the probes hit `GET /ping` (`po-token-server.yaml:38-51`). I could not check the upstream image's user, whether its working directory is writable by uid 1000, or that `/ping` exists. If any of these is wrong, the pod crash-loops or never becomes Ready, with the same Kustomization effect as above.
- **Plugin version.** The claim that MA 2.10.x `pip install`s `bgutil-ytdlp-pot-provider` unpinned at runtime decides whether `2.x` is the right range. It is external behaviour I could not check. MA on main is now 2.10.8 (`deployment.yaml`), not 2.10.7 as the PR body says.
## 🟡 Suggestions
- **CNP location.** `kubernetes/apps/music-assistant/po-token-server.yaml:73-114` puts the CiliumNetworkPolicy in the app directory. Every other hand-written CNP lives in `kubernetes/infrastructure/network-policies/<ns>-ns.yaml` and is listed in that directory's `kustomization.yaml` (navidrome, kuma-sync, review-bot, etc.). The only in-app CNPs are the ones authentik-gate-sync generates. Moving it to a new `music-assistant-ns.yaml` keeps policy review in one place, and the later per-workload egress audit of `music-assistant` that #1432 deferred will find it there.
- **World egress includes the LAN.** `po-token-server.yaml:108-114` allows `toEntities: world` on 443. #1432 describes `world` in this cluster as "internet + LAN". This third-party image is now auto-bumped across a whole major with no human in the loop, so the pod can reach every HTTPS service on the LAN (UDM, etc.). The PR explains why it didn't use `toFQDNs`. A middle ground would be `toCIDRSet` with `0.0.0.0/0` / `::/0` and `except` the RFC1918/ULA ranges, or FQDN patterns for `*.google.com` / `*.youtube.com` / `*.googlevideo.com`. Either keeps the "unknown Google endpoints" flexibility without reaching into the LAN.
## 🟢 Nits
- The PR body says MA 2.10.7; main runs 2.10.8.
- Nothing monitors the new server (no KumaMonitor, no alert). A broken token server only shows up as YouTube Music playback failures in MA.
## Tests
The repo validates manifests in CI (`ci-manifests-test`), which was still pending on the head sha when I checked. The PR says it ran `kubectl kustomize` on both kustomizations, which is the expected check for a manifest-only change. There is no unit-test surface here. After merge, check:
- the pod becomes Ready under uid 1000;
- `hubble observe --verdict DROPPED -n music-assistant` shows no drops between MA and `po-token-server:4416`, or from the server to Google;
- the ImagePolicy resolves to a bare `2.x.y` tag.
## Recommendation
Looks reasonable — no blocking issues; comments above are optional.
<!-- pr-review:sha=96ea51014393696fad95728e9efb596d380778a0 -->
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Adds a bgutil PO token server next to Music Assistant so MA's YouTube Music provider can stream tracks with no local copy (MA prefers the local file when it has one).
kubernetes/apps/music-assistant/po-token-server.yaml: Deployment (non-root uid 1000, drop ALL, RuntimeDefault seccomp), ClusterIP Service on :4416, pod-scoped CNP: ingress only from the MA pod on 4416 plus kubelet probes; egress to DNS and world:443 only (the server is unauthenticated). Egress uses world:443 rather than toFQDNs because the full set of Google endpoints BotGuard needs isn't documented.bgutil-pot-provider: bare-semver tags (node flavor), range2.x. MA 2.10.7pip installsbgutil-ytdlp-pot-providerunpinned at runtime (2.0.2 on PyPI today), so the server has to stay on the same major. The MA docs' "1.2.1" predates the 2.x plugin line.Manual step after merge (can't be automated): MA UI → Settings → Providers → add YouTube Music with a Premium account cookie and PO token server URL
http://po-token-server.music-assistant.svc.cluster.local:4416. The cookie expires periodically and must then be re-entered.Verified:
kubectl kustomizerenders bothapps/music-assistantandinfrastructure/image-automationon current main.🤖 Generated with Claude Code
🤖 Automated review — model
claude-opus-5-5, commit96ea51014393696fad95728e9efb596d380778a0.Summary
This PR adds a bgutil PO token server (Deployment, ClusterIP Service on :4416 and a pod-scoped CiliumNetworkPolicy) to the
music-assistantnamespace so Music Assistant's YouTube Music provider can stream. It also enrols the server's image in Flux image automation (2.x, bare-semver filter). The manifests are consistent with the repo:app.kubernetes.io/name: music-assistant,deployment.yaml).sbrNAD), so the ClusterIP is reachable.music-assistantout of scope; there is nomusic-assistant-ns.yaml).GitHub reports the PR as mergeable. Since the PR base
cb697e2, main has only gained the CoreDNS change (#1450), which touches none of these files. No other PR or code adds a PO token server.⚪ Unverified
docker.io/brainicism/bgutil-ytdlp-pot-provider:2.0.2exists, or that bare-semver tags are the node build. Both claims rest only on the PR body. Themusic-assistantFlux Kustomization runs withwait: trueandtimeout: 30m(kubernetes/clusters/homelab/music-assistant.yaml). A bad tag would leave that Kustomization not-Ready, although MA itself keeps running.po-token-server.yaml:24-29forces uid/gid 1000 and drops ALL capabilities, and the probes hitGET /ping(po-token-server.yaml:38-51). I could not check the upstream image's user, whether its working directory is writable by uid 1000, or that/pingexists. If any of these is wrong, the pod crash-loops or never becomes Ready, with the same Kustomization effect as above.pip installsbgutil-ytdlp-pot-providerunpinned at runtime decides whether2.xis the right range. It is external behaviour I could not check. MA on main is now 2.10.8 (deployment.yaml), not 2.10.7 as the PR body says.🟡 Suggestions
kubernetes/apps/music-assistant/po-token-server.yaml:73-114puts the CiliumNetworkPolicy in the app directory. Every other hand-written CNP lives inkubernetes/infrastructure/network-policies/<ns>-ns.yamland is listed in that directory'skustomization.yaml(navidrome, kuma-sync, review-bot, etc.). The only in-app CNPs are the ones authentik-gate-sync generates. Moving it to a newmusic-assistant-ns.yamlkeeps policy review in one place, and the later per-workload egress audit ofmusic-assistantthat #1432 deferred will find it there.po-token-server.yaml:108-114allowstoEntities: worldon 443. #1432 describesworldin this cluster as "internet + LAN". This third-party image is now auto-bumped across a whole major with no human in the loop, so the pod can reach every HTTPS service on the LAN (UDM, etc.). The PR explains why it didn't usetoFQDNs. A middle ground would betoCIDRSetwith0.0.0.0/0/::/0andexceptthe RFC1918/ULA ranges, or FQDN patterns for*.google.com/*.youtube.com/*.googlevideo.com. Either keeps the "unknown Google endpoints" flexibility without reaching into the LAN.🟢 Nits
Tests
The repo validates manifests in CI (
ci-manifests-test), which was still pending on the head sha when I checked. The PR says it rankubectl kustomizeon both kustomizations, which is the expected check for a manifest-only change. There is no unit-test surface here. After merge, check:hubble observe --verdict DROPPED -n music-assistantshows no drops between MA andpo-token-server:4416, or from the server to Google;2.x.ytag.Recommendation
Looks reasonable — no blocking issues; comments above are optional.