From 753c48be247af97e66508fb6a9976b8fdb5e956d Mon Sep 17 00:00:00 2001 From: Keith Jackson Date: Thu, 1 Oct 2026 22:26:35 -0500 Subject: [PATCH 1/4] Added arr suite ability to connect to plex --- catalog/radarr.yaml | 3 +++ catalog/sonarr.yaml | 3 +++ 2 files changed, 6 insertions(+) diff --git a/catalog/radarr.yaml b/catalog/radarr.yaml index ce90d7b..e9c2c56 100644 --- a/catalog/radarr.yaml +++ b/catalog/radarr.yaml @@ -100,6 +100,9 @@ dependencies: - to: jackett # arr → indexer proxy (docs/13 dependency graph; R-NET-02 explicit allow) port: 9117 protocol: tcp + - to: plex + port: 32400 # arr -> plex: required since library is on NFS which does not provide file CRUD notifications + protocol: tcp ingressRoutes: - host: radarr.${BASE_DOMAIN} port: 7878 # Radarr web/API; TLS terminates at the shared Gateway (docs/08, docs/09) diff --git a/catalog/sonarr.yaml b/catalog/sonarr.yaml index a91f2fb..58d1f26 100644 --- a/catalog/sonarr.yaml +++ b/catalog/sonarr.yaml @@ -100,6 +100,9 @@ dependencies: - to: jackett # arr → indexer proxy (docs/13 dependency graph; R-NET-02 explicit allow) port: 9117 protocol: tcp + - to: plex # arr -> plex: required since library is on NFS which does not provide file CRUD notifications + port: 32400 + protocol: tcp ingressRoutes: - host: sonarr.${BASE_DOMAIN} port: 8989 # Sonarr web/API; TLS terminates at the shared Gateway (docs/08, docs/09) From 9048c2f5e89e77fcf8c44a44aae782051bd8e10e Mon Sep 17 00:00:00 2001 From: Keith Jackson Date: Thu, 1 Oct 2026 22:35:47 -0500 Subject: [PATCH 2/4] Forgot to render the golden from the catalog -- fixed --- apps/wave-0/plex.yaml | 44 +++++++++++++++++++++++++++++++++++++++++ apps/wave-1/radarr.yaml | 22 +++++++++++++++++++++ apps/wave-1/sonarr.yaml | 22 +++++++++++++++++++++ 3 files changed, 88 insertions(+) diff --git a/apps/wave-0/plex.yaml b/apps/wave-0/plex.yaml index 2928eaa..0cb6292 100644 --- a/apps/wave-0/plex.yaml +++ b/apps/wave-0/plex.yaml @@ -173,6 +173,28 @@ spec: port: 32400 targetPort: 32400 --- +# CiliumNetworkPolicy reciprocal ingress allow (R-NET-02): plex-allow-radarr-32400 +apiVersion: cilium.io/v2 +kind: CiliumNetworkPolicy +metadata: + name: plex-allow-radarr-32400 + namespace: plex + labels: + app.kubernetes.io/name: plex +spec: + endpointSelector: + matchLabels: + app.kubernetes.io/name: plex + ingress: + - fromEndpoints: + - matchLabels: + app.kubernetes.io/name: radarr + k8s:io.kubernetes.pod.namespace: radarr + toPorts: + - ports: + - port: '32400' + protocol: TCP +--- # CiliumNetworkPolicy reciprocal ingress allow (R-NET-02): plex-allow-seerr-32400 apiVersion: cilium.io/v2 kind: CiliumNetworkPolicy @@ -195,6 +217,28 @@ spec: - port: '32400' protocol: TCP --- +# CiliumNetworkPolicy reciprocal ingress allow (R-NET-02): plex-allow-sonarr-32400 +apiVersion: cilium.io/v2 +kind: CiliumNetworkPolicy +metadata: + name: plex-allow-sonarr-32400 + namespace: plex + labels: + app.kubernetes.io/name: plex +spec: + endpointSelector: + matchLabels: + app.kubernetes.io/name: plex + ingress: + - fromEndpoints: + - matchLabels: + app.kubernetes.io/name: sonarr + k8s:io.kubernetes.pod.namespace: sonarr + toPorts: + - ports: + - port: '32400' + protocol: TCP +--- # CiliumNetworkPolicy gateway-ingress allow (R-NET-01/02): plex-allow-gateway-ingress apiVersion: cilium.io/v2 kind: CiliumNetworkPolicy diff --git a/apps/wave-1/radarr.yaml b/apps/wave-1/radarr.yaml index 2cef42c..cede07f 100644 --- a/apps/wave-1/radarr.yaml +++ b/apps/wave-1/radarr.yaml @@ -281,6 +281,28 @@ spec: - port: '9117' protocol: TCP --- +# CiliumNetworkPolicy egress allow (R-NET-02): radarr-allow-plex-32400 +apiVersion: cilium.io/v2 +kind: CiliumNetworkPolicy +metadata: + name: radarr-allow-plex-32400 + namespace: radarr + labels: + app.kubernetes.io/name: radarr +spec: + endpointSelector: + matchLabels: + app.kubernetes.io/name: radarr + egress: + - toEndpoints: + - matchLabels: + app.kubernetes.io/name: plex + k8s:io.kubernetes.pod.namespace: plex + toPorts: + - ports: + - port: '32400' + protocol: TCP +--- # CiliumNetworkPolicy reciprocal ingress allow (R-NET-02): radarr-allow-seerr-7878 apiVersion: cilium.io/v2 kind: CiliumNetworkPolicy diff --git a/apps/wave-1/sonarr.yaml b/apps/wave-1/sonarr.yaml index 3771672..9f14bd9 100644 --- a/apps/wave-1/sonarr.yaml +++ b/apps/wave-1/sonarr.yaml @@ -281,6 +281,28 @@ spec: - port: '9117' protocol: TCP --- +# CiliumNetworkPolicy egress allow (R-NET-02): sonarr-allow-plex-32400 +apiVersion: cilium.io/v2 +kind: CiliumNetworkPolicy +metadata: + name: sonarr-allow-plex-32400 + namespace: sonarr + labels: + app.kubernetes.io/name: sonarr +spec: + endpointSelector: + matchLabels: + app.kubernetes.io/name: sonarr + egress: + - toEndpoints: + - matchLabels: + app.kubernetes.io/name: plex + k8s:io.kubernetes.pod.namespace: plex + toPorts: + - ports: + - port: '32400' + protocol: TCP +--- # CiliumNetworkPolicy reciprocal ingress allow (R-NET-02): sonarr-allow-seerr-8989 apiVersion: cilium.io/v2 kind: CiliumNetworkPolicy From 58f02fe8b1f9303e2073b5a01b5e75541dc52170 Mon Sep 17 00:00:00 2001 From: Keith Jackson Date: Thu, 1 Oct 2026 22:53:04 -0500 Subject: [PATCH 3/4] Removed trailing whitespace --- catalog/radarr.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/catalog/radarr.yaml b/catalog/radarr.yaml index e9c2c56..51ad41b 100644 --- a/catalog/radarr.yaml +++ b/catalog/radarr.yaml @@ -100,7 +100,7 @@ dependencies: - to: jackett # arr → indexer proxy (docs/13 dependency graph; R-NET-02 explicit allow) port: 9117 protocol: tcp - - to: plex + - to: plex port: 32400 # arr -> plex: required since library is on NFS which does not provide file CRUD notifications protocol: tcp ingressRoutes: From 8a094c46f78befcdea0334dc76412b7051592516 Mon Sep 17 00:00:00 2001 From: Keith Jackson Date: Thu, 1 Oct 2026 23:09:08 -0500 Subject: [PATCH 4/4] Added decision record for allowing arr apps to connect to plex --- decisions/2026-10-01-arr-to-plex.md | 120 ++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 decisions/2026-10-01-arr-to-plex.md diff --git a/decisions/2026-10-01-arr-to-plex.md b/decisions/2026-10-01-arr-to-plex.md new file mode 100644 index 0000000..3dbdf35 --- /dev/null +++ b/decisions/2026-10-01-arr-to-plex.md @@ -0,0 +1,120 @@ +--- +date: 2026-10-01 +branch: bugfix/arr-to-plex +title: Radarr and Sonarr may now reach Plex on 32400 — the network path only, not the Plex connection itself +--- + +## What changed + +Radarr and Sonarr had no route to Plex. The media library lives on NFS +(`/mnt/Storage/plex`, the ZFS dataset `Storage/plex` on `${BULK_NFS_SERVER}`), and NFS +does not deliver filesystem CRUD notifications, so Plex never learns that an arr has +written a new file. Without a push from the arrs, the library only reflects new media +when somebody scans it by hand. That is the bug this branch addresses. + +The fix is one `dependencies:` entry per app, added to the catalog — the source of truth: + + - to: plex + port: 32400 + protocol: tcp + +in both `catalog/radarr.yaml` and `catalog/sonarr.yaml` (these two *are* the arr suite +here; `jackett`, `qbittorrent` and `seerr` are different roles and are untouched). + +The renderer expands each entry into a **pair** of Cilium policies, which is why a +three-line catalog change produces 88 lines under `apps/`: + +| Side | Policy | File | +|---|---|---| +| egress | `radarr-allow-plex-32400` | `apps/wave-1/radarr.yaml` | +| egress | `sonarr-allow-plex-32400` | `apps/wave-1/sonarr.yaml` | +| reciprocal ingress | `plex-allow-radarr-32400` | `apps/wave-0/plex.yaml` | +| reciprocal ingress | `plex-allow-sonarr-32400` | `apps/wave-0/plex.yaml` | + +Three commits, and the middle two are worth recording because both were self-inflicted +gate failures rather than design changes: + +- `753c48b` — the catalog edit alone, with `apps/` left unrendered. +- `9048c2f` — re-rendered `apps/` from the catalog, which `render==golden` requires. +- `58f02fe` — removed 20 trailing spaces on `catalog/radarr.yaml:103`, left behind as + comment-alignment padding when the comment moved down to the `port:` line. Sonarr's + equivalent line kept its comment in place, which is why only radarr tripped. + +## What I verified + +**The lint failure, root-caused.** Gate run 193 on `9048c2f` failed with +`[fail] lint: FAILED -> blocks merge`, and lint was the only failing stage. yamllint +emitted 419 findings, but **418 were `line-length`**, which `ci/yamllint.yaml` pins at +`level: warning` and which therefore cannot change the exit code. The single error-level +finding in the entire repository was: + + catalog/radarr.yaml + 103:13 error trailing spaces (trailing-spaces) + +`trailing-spaces` inherits the default severity (error), so that one line returned rc 1. +After the fix, `git grep -nIE ' +$' -- '*.yaml' '*.yml'` matches nothing repo-wide. + +**Gate run 195 on `58f02fe`** (read from the job log, not from the status tick): + + [ ok ] approval-policy: green [ ok ] l0: green + [ ok ] gitea-settings: green [ ok ] render-golden: green + [ ok ] decisions-log: green [ ok ] l1: green + [ ok ] lint: green + [fail] MERGE GATE: UNDETERMINED — greenness was NOT ESTABLISHED. + [fail] Stages that did not run: spec-immutability + +The Gitea check reports `success` / "Successful in 1m41s", but the gate's own verdict is +**UNDETERMINED (rc 3)**, not PASS. That tick is `gate.yml` tolerating exit 3 for the one +stage it documents as unverifiable on this runner (the spec repo is not checked out +there). Nothing else produced a 3. + +**`render==golden` green** on `58f02fe`, so `apps/` is a faithful canonical render of +`catalog/` — the four generated policies are not hand-written drift. + +**DNS egress was already open** and is not a new gap: `apps/wave-1/radarr.yaml` carries +an existing allow for port 53 UDP+TCP, so resolving the Plex service name does not need +anything added here. + +**Live branch protection on `main`**, measured against +`/api/v1/repos/Home-Network/home-network-k3s-impl/branches/main` rather than assumed: +`protected: true`, `user_can_push: false`, `enable_status_check: true`, +`status_check_contexts: ['gate / gate (pull_request)']`, `required_approvals: 1`. + +## What I did NOT verify + +**This change does not accomplish what the PR set out to do.** It opens the network path +and nothing else. Triggering a library update also requires each arr's +*Settings → Connect → Plex Media Server* entry plus a Plex token, and **none of that +exists anywhere in `catalog/` or `apps/`** — I grepped both for `plex`, `token` and +`connect` and found only NFS paths, a uid and a timezone. Radarr's and Sonarr's config +lives on a 5Gi Longhorn PVC mounted at `/config`, so that connection is UI-only state +that exists solely on the PVC and never in git, in the same way Crafty's local login +does. **Until somebody adds it in each app's UI, Plex still will not see library +updates, and this branch will look like it did nothing.** That follow-up is not tracked +by any file in this PR. + +**L2/L3 conformance never ran.** `CLUSTER_AVAILABLE` is unset on the runner, so the +eight requirements this diff touches — `R-NET-01`, `R-NET-05`, `R-NET-06`, `R-NET-07`, +`R-IDP-02`, `R-IDP-06`, `R-GITOPS-02`, `R-PLAT-02` — are **UNVERIFIED**. That gap is +unusually load-bearing here: the entire substance of this change is four +CiliumNetworkPolicies, and no test has confirmed that traffic from the `radarr` or +`sonarr` namespaces actually reaches `plex:32400`. The green gate proves the offline +render subset only. + +**`spec-immutability` did not run** (`R-TEST-06` not verified), for the documented +reason — the sibling spec repo is absent from the runner. + +**Nothing was checked on the cluster.** I ran no `kubectl`, so I did not confirm that +the `plex` Service exposes 32400 as the arrs would address it, that the pod labels the +policies select (`app.kubernetes.io/name`, `k8s:io.kubernetes.pod.namespace`) match the +running pods, or that Cilium accepted the four new objects. Flux has not applied any of +this: the branch is unmerged, so `.status.lastAppliedRevision` does not contain it. + +**I did not verify Plex's own side** — whether its library-update endpoint on 32400 is +the right target for the arrs' notification, or whether Plex requires +"Update my library automatically" enabled to act on one. + +**I am not the approver and did not approve this PR.** `required_approvals: 1` is live, +and the only credential on the authoring workstation belongs to the PR's author +(`jakman85`), whose own review Gitea does not count. Per CLAUDE.md §1 the merge is a +human action regardless.