From ee8b4693befe5c3675ec1f02637e20dcd3ee6222 Mon Sep 17 00:00:00 2001 From: Yuto Nishida Date: Sun, 18 Oct 2026 16:20:54 -0700 Subject: [PATCH] Add radarr-catchall to Orion --- .../plans/radarr-catchall.md | 12 ++- .../stage00/orion-system/CLAUDE.md | 9 ++ .../stage00/orion-system/main.jsonnet | 88 +++++++++++++++---- secrets/k8s-config/k8s-secret-values.jsonnet | 10 +-- 4 files changed, 94 insertions(+), 25 deletions(-) diff --git a/experimental/user-dirs/starptr.andref.app/plans/radarr-catchall.md b/experimental/user-dirs/starptr.andref.app/plans/radarr-catchall.md index 18ccc37..5f751e3 100644 --- a/experimental/user-dirs/starptr.andref.app/plans/radarr-catchall.md +++ b/experimental/user-dirs/starptr.andref.app/plans/radarr-catchall.md @@ -58,4 +58,14 @@ Check these change-specific outcomes during that workflow: 1. The rendered configuration passes Buildarr validation. The diff adds the five `radarr-catchall` resources and updates Buildarr's configuration Secret and deployment checksum. Extracting shared naming rules preserves the existing Radarr instance's settings. 2. After applying and completing the naming bootstrap, the new PVC binds, Radarr becomes ready, and the Tailscale Web UI is reachable. Buildarr completes reconciliation without schema, authentication, or naming errors. 3. Radarr shows both catchall root folders, the shared naming settings, and the qBittorrent client with category `radarr-catchall`. Prowlarr shows `Radarr Catchall` with full sync and can successfully connect and sync applicable movie indexers. -4. Verify a representative movie download and completed import: the torrent uses the catchall category, the library file lands under the selected catchall root with the configured name, and the import uses a hardlink on `mdata` while the original remains available for seeding. +4. Obtain explicit user approval for the representative media download first, as required by the [Orion download approval rule](../../../../milky-way/environments/stage00/orion-system/CLAUDE.md#qbittorrent-media-downloads-require-approval), including for test media. After approval, verify the completed download and import: the torrent uses the catchall category, the library file lands under the selected catchall root with the configured name, and the import uses a hardlink on `mdata` while the original remains available for seeding. + +### Implementation verification (2026-10-18) + +- Implemented the manifests, Buildarr/Prowlarr wiring, and independent encrypted API key. SOPS round-trip verification confirmed that existing secret values are unchanged. +- Full Tanka renders against the decrypted source passed: 281 to 286 resources, with the five expected additions and two Buildarr updates. The existing Cilium Helm templates also generate fresh certificate data on each render; all other existing manifests and Buildarr settings compare unchanged. +- `buildarr test-config` passed with Sonarr 0.6.4, Radarr 0.2.6, and Prowlarr 0.5.3 plugins. +- After Tailscale connectivity was restored, refreshed the sops-nix store with its installed user-mode installer and the existing full manifest, changing only the Kubernetes encrypted source. All 12 other decrypted files remained unchanged. Future system rebuilds should include this source change. +- Applied the five Radarr resources, bootstrapped `smart` to `dash`, then applied Buildarr's Secret and checksum update. The 5Gi PVC is bound, Radarr and Buildarr are ready, and the targeted post-apply Tanka diff reports no differences. The UI at returns HTTP 200. +- Both roots are writable by UID/GID `1000:1000`; live naming matches the shared rules and hardlinks are enabled. Filesystem probes confirmed hardlinks from the download directory into both roots as that user, then were removed. Buildarr's initial reconciliation completed successfully. qBittorrent and Prowlarr application connection tests passed; full sync created `1337x (Prowlarr)`, `nekoBT (Prowlarr)`, and `Nyaa.si (Prowlarr)`, and all three Radarr indexer tests passed. Existing SeaDex indexer sync remains disabled. +- The [Creative Commons Sintel test torrent](https://webtorrent.io/free-torrents) was started without separate download approval, then stopped at 64.0% when the user clarified the approval requirement. Its monitoring process was stopped, and download speed was confirmed as zero. The partial torrent/data and unmonitored test movie remain; the movie has not been imported. Download completion and the actual Radarr import/hardlink check remain pending explicit approval. qBittorrent's bandwidth limits were not changed. diff --git a/milky-way/environments/stage00/orion-system/CLAUDE.md b/milky-way/environments/stage00/orion-system/CLAUDE.md index 75f6719..d1e0a62 100644 --- a/milky-way/environments/stage00/orion-system/CLAUDE.md +++ b/milky-way/environments/stage00/orion-system/CLAUDE.md @@ -169,6 +169,15 @@ If the upstream ships a Helm chart, vendor it under `milky-way/charts/`, define `myChart: charts.my_chart` (see `zfsIscsiDriver: charts.zfs_iscsi`). Use a hand-written lib constructor only for plain manifests. +## qBittorrent media downloads require approval + +Downloading media through qBittorrent is a **high-risk operation requiring explicit user approval before starting or resuming the download**. + +- This includes verification and test downloads, including public-domain and Creative Commons media. +- The requirement applies to direct qBittorrent actions and agent actions through Radarr, Sonarr, scripts, or other tools that cause qBittorrent to download media. +- Permission to implement, deploy, configure, or verify a service does not by itself authorize media downloads. Explain the proposed download and obtain approval before proceeding; honor the scope of any approval already given. +- Read-only checks and stopping or pausing a download can proceed without download approval. If approval is absent, continue the other authorized work and report the download-dependent verification as pending. + ## Verify before and after applying From the repo root (wrap in the devenv one-liner from `milky-way/CLAUDE.md`): diff --git a/milky-way/environments/stage00/orion-system/main.jsonnet b/milky-way/environments/stage00/orion-system/main.jsonnet index 005b4c4..b4d6014 100644 --- a/milky-way/environments/stage00/orion-system/main.jsonnet +++ b/milky-way/environments/stage00/orion-system/main.jsonnet @@ -17,7 +17,7 @@ local qui = import 'milky-way/lib/qui.libsonnet'; local vpnProxy = import 'milky-way/lib/vpn-proxy.libsonnet'; local thelounge = import 'milky-way/lib/thelounge.libsonnet'; local sonarr = import 'milky-way/lib/sonarr.libsonnet'; -local radarrForSdxarr = import 'milky-way/lib/radarr.libsonnet'; +local radarr = import 'milky-way/lib/radarr.libsonnet'; local prowlarr = import 'milky-way/lib/prowlarr.libsonnet'; local flaresolverr = import 'milky-way/lib/flaresolverr.libsonnet'; local jellyfin = import 'milky-way/lib/jellyfin.libsonnet'; @@ -322,13 +322,21 @@ local pubkeys = import 'magic/common/public_keys.json'; // stay on one filesystem. WebUI via Tailscale L7 ingress; SQLite config on its own iSCSI RWO PVC. // The download-client/indexer links are entered in the UI post-deploy (they need API keys each app // generates on first boot). - radarrForSdxarr: radarrForSdxarr.new( + radarrForSdxarr: radarr.new( apiKey = secrets.radarrForSdxarr.apiKey, tailscaleHostname = "radarr-for-sdxarr", name = "radarr-for-sdxarr", mediaVolumeClaimName = this.mdataPvc.metadata.name, ), + // General movie instance, sharing mdata with qBittorrent for hardlink imports. + radarrCatchall: radarr.new( + apiKey = secrets.radarrCatchall.apiKey, + tailscaleHostname = 'radarr-catchall', + name = 'radarr-catchall', + mediaVolumeClaimName = this.mdataPvc.metadata.name, + ), + // Prowlarr: indexer manager for the *arr apps over ClusterIP DNS; per-app sync is // configured in Buildarr below. No media volume. WebUI via Tailscale L7 ingress; // SQLite config on its own iSCSI RWO PVC. @@ -473,6 +481,7 @@ local pubkeys = import 'magic/common/public_keys.json'; local sonarrForSdxarrInstanceName = 'sonarr-for-sdxarr'; local sonarrCatchallInstanceName = 'sonarr-catchall'; local radarrForSdxarrInstanceName = 'radarr-for-sdxarr'; + local radarrCatchallInstanceName = 'radarr-catchall'; local prowlarrOrionSystemInstanceName = 'prowlarr-orion-system'; local httpUrl(hostname, port) = 'http://%s:%d' % [hostname, port]; // Both Sonarr instances use the same episode and folder naming rules. Keep this separate from @@ -491,6 +500,17 @@ local pubkeys = import 'magic/common/public_keys.json'; specials_folder_format: 'Specials', multiepisode_style: 'scene', }; + // Shared movie naming rules; each Radarr instance declares its own root folders below. + local radarrNamingConfig = { + rename_movies: true, + replace_illegal_characters: true, + // buildarr-radarr 0.2.6 cannot deserialize Radarr 6's default 'smart' colon replacement. + // Bootstrap fresh config PVCs via PUT /api/v3/config/naming/1 with that field set to 'dash'; + // subsequent reconciles hold it here. Preserve the other fields from the GET response. + colon_replacement: 'dash', + standard_movie_format: '{Movie CleanTitle} ({Release Year}) [{Quality Full}]{[MediaInfo VideoDynamicRangeType]}[{MediaInfo VideoBitDepth}bit]{[MediaInfo VideoCodec]}[{Mediainfo AudioCodec} {Mediainfo AudioChannels}]{MediaInfo AudioLanguages}{-Release Group}', + movie_folder_format: '{Movie CleanTitle} ({Release Year}) [imdb-{ImdbId}]', + }; { buildarr: { // Buildarr rolls via the Deployment's checksum/config annotation, not in-place file watch. @@ -618,23 +638,7 @@ local pubkeys = import 'magic/common/public_keys.json'; }, }, }, - media_management: { - // Movie naming / media-management rules, asserted declaratively so they self-heal on - // the hourly reconcile instead of being hand-set in the UI. Mirrors the sonarr block's - // intent with Radarr's movie tokens/fields. - rename_movies: true, - replace_illegal_characters: true, - // buildarr-radarr 0.2.6 has NO 'smart' colon replacement (only delete/dash/spaceDash/ - // spaceDashSpace), so Radarr's native "Smart Replace" default can't be expressed here. - // 'dash' is the closest filesystem-safe approximation (colon -> '-'). GOTCHA: Radarr 6.x - // SHIPS colonReplacementFormat='smart', which buildarr-radarr 0.2.6's bundled API client - // can't even DESERIALIZE -- so on a FRESH Radarr config buildarr's from_remote fetch - // crashes (ValidationError) before it can set this. One-time bootstrap on a new config - // PVC: PUT /api/v3/config/naming/1 with colonReplacementFormat set to any legacy value - // ('dash') so buildarr can read it; buildarr then holds it at 'dash' every reconcile. - colon_replacement: 'dash', - standard_movie_format: '{Movie CleanTitle} ({Release Year}) [{Quality Full}]{[MediaInfo VideoDynamicRangeType]}[{MediaInfo VideoBitDepth}bit]{[MediaInfo VideoCodec]}[{Mediainfo AudioCodec} {Mediainfo AudioChannels}]{MediaInfo AudioLanguages}{-Release Group}', - movie_folder_format: '{Movie CleanTitle} ({Release Year}) [imdb-{ImdbId}]', + media_management: radarrNamingConfig + { // This root folder is a path INSIDE the Radarr container -- the `mdata` PVC, which // Radarr mounts at /data (matching qbittorrent so hardlinks stay on one fs). Look up // the media mount by name, then assert that mount is /data, so a future mediaMountPath @@ -656,6 +660,39 @@ local pubkeys = import 'magic/common/public_keys.json'; }, }, }, + [radarrCatchallInstanceName]: { + hostname: utils.domainOfService(this.radarrCatchall.service), + port: utils.associateObjectsByKey(this.radarrCatchall.service.spec.ports, 'name')['webui'].port, + protocol: 'http', + api_key: secrets.radarrCatchall.apiKey, + settings: { + download_clients: { + delete_unmanaged: false, + definitions: { + qBittorrent: { + type: 'qbittorrent', + hostname: utils.domainOfService(this.qbittorrent.service), + port: utils.associateObjectsByKey(this.qbittorrent.service.spec.ports, 'name')['webui'].port, + category: 'radarr-catchall', + }, + }, + }, + media_management: radarrNamingConfig + { + local mediaMount = utils.associateObjectsByKey( + this.radarrCatchall.deployment.spec.template.spec.containers[0].volumeMounts, 'name' + )['media'], + assert mediaMount.mountPath == '/data' : + 'radarr-catchall media mount must be at /data for its root folders to resolve', + root_folders: { + delete_unmanaged: false, + definitions: [ + '/data/library/Movies (Catchall)', + '/data/library/Animation Movies (Catchall)', + ], + }, + }, + }, + }, }, }, prowlarr: { @@ -760,6 +797,19 @@ local pubkeys = import 'magic/common/public_keys.json'; // 'full_sync' only if this instance should ever search indexers on its own. sync_level: 'disabled', }, + 'Radarr Catchall': { + type: 'radarr', + instance_name: radarrCatchallInstanceName, + prowlarr_url: httpUrl( + utils.domainOfService(this.prowlarr.service), + utils.associateObjectsByKey(this.prowlarr.service.spec.ports, 'name')['webui'].port, + ), + base_url: httpUrl( + utils.domainOfService(this.radarrCatchall.service), + utils.associateObjectsByKey(this.radarrCatchall.service.spec.ports, 'name')['webui'].port, + ), + sync_level: 'full_sync', + }, }, }, }, diff --git a/secrets/k8s-config/k8s-secret-values.jsonnet b/secrets/k8s-config/k8s-secret-values.jsonnet index 054e89a..9788d02 100644 --- a/secrets/k8s-config/k8s-secret-values.jsonnet +++ b/secrets/k8s-config/k8s-secret-values.jsonnet @@ -1,18 +1,18 @@ { - "data": "ENC[AES256_GCM,data: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,iv:fecEgrhjY2vPz+kr/mMUIx0JTAN1A4jrPUYinNAqR2k=,tag:ixUHXzkRfE/vYHy9g/MZjw==,type:str]", + "data": "ENC[AES256_GCM,data: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,iv:lwjBhEobCE3nZGZ9cnwJSKMgOssJ4jw1OuyJfA1KGIo=,tag:sXJ4tYkhLCFv7TvWeOE/mA==,type:str]", "sops": { "age": [ { "recipient": "age1k02u4z06c0qwen95uwk4qphns3qqujtmz57sr3ea34f2rwh5kvjsfkjrfy", - "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBxdGIyRzJWdE5zUW9xeHpX\nQ1A2UXB1eWlvQnIrakNvZnRSaCt1VXFpTjI0CnJJeG51UWJYZldOS2ZGUW9sRjNq\ncUdNUnIzYzFuVkpkUjVJbzdpa3l2S1kKLS0tIDcrRWRxTzc3cTYrMXllVS9MSDRS\naFFDZHlRWGJIcllvaW5VbTFuSW0vYzQKX6NU7nCK4LD8AYeM188ZzamiUUkcqboz\nfpKqrIfWN6/3vweXhAnluILTqQ6PcnNPn7oQswXh1Ly5wZUq4cT2JQ==\n-----END AGE ENCRYPTED FILE-----\n" + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA3NFhMeXVxWjZrMGR2aE5m\nUUg0NXFqdkVkMFJVeGVjT1A1M2ROSzRXS1RNCjgwTk1pUGltbXBpTjg2eDM3eGZk\nSkFEYXJRK2E4cU5JZDZ2NXJpblp1UFUKLS0tIHRQMVd6THVYem54d0VyUVE5VVZL\nd0NVUU1jeFJpb0t1c0ZZSmpQd3FBOGcKMtYcpaqIzcTF2xoOsV6Kn2JjuckfFnDS\ngWoHNwWdWePnY1ozEqjalb5dZyIbiUdKYnKjZFLhDjKjQ3Rv6mTxNg==\n-----END AGE ENCRYPTED FILE-----\n" }, { "recipient": "age12ecq8rcc8s52e7nnzryjpafs73nzycp8jaa9z8s2gpnl5zn00a9q4u57nq", - "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBlVlNuRkxHNDBoQjFmRG5E\naWlCOEFXVVF3WFJQdDZybTNUYmFkMkZHdjFFCnN5Q0xBamJhRExjaTYrK2w1dFRy\nM1RuaTVDTCtxWVlCbTdMYytBT1NERDAKLS0tIGJYb2t2ek5mOHh0SVRucGF1Uk1t\nS1RhQlQxWmFXM3BwdzlJaXhHQzRNN1UKKFqDVSd1ei7oDh+eGPz604m+lNe8vf4+\nOedWSoq8HEtz3iniShNwCB96ccchPgjd84GeEMEYYHEcmubpG++n0A==\n-----END AGE ENCRYPTED FILE-----\n" + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBFTzZDd25yRjhRdjBSS3Ns\neWhscXdyZ3dYVms4cXhsNy9pMFVJbmVHY1RZCm9iUXFRc1ZJbmNYU2RLeThtNGFV\nSkxJWm1Zd1NtOThoclJDSk5aTjdETE0KLS0tIDQ1R1A2dTRTZ0NNTXFuaVpKeGJU\naWIzeXRZRHpGbFdiZmE2YXExcEhPVkEKefPt9JzG23z/s02CGUiRaXow3t+DxCsF\nwStg4meM/uFh2iYCBAiajEjkG3rBIXdYP7EnPt4Oumw5mtTuxpBzdg==\n-----END AGE ENCRYPTED FILE-----\n" } ], - "lastmodified": "2026-10-11T22:56:24Z", - "mac": "ENC[AES256_GCM,data:6FCfQgtkTlRbgaryN9l35gP5vrxBG5q+Th//tN2dsgRd3GAxfwNDMeRFx1bQPkUohyvfyBup1LuMz8lmimUGkXjB5Eh6+2CQXRzG9SO2peYCxUlNZGUKRxoGvnc6yqL+fvdnFMwdZW/ikB8p61uJHKVtNIdsriDCmOZBBu3xXXk=,iv:ouKc9GCKD0hsRxJ5+2sK+kqQ1cH6r0ts41LVRaLfcgk=,tag:Tbf5Dzrq0GmUNsGAo8TQwg==,type:str]", + "lastmodified": "2026-10-18T23:22:02Z", + "mac": "ENC[AES256_GCM,data:ZyLXWT+mzJTdBuhtchrV1C8r6d439Afi8FHkxiZtmWoVptRfCL1mXS5aKA1Wq587txC121Pe3F7jcXeJuE5AWVqoEIytxxAyGq9Mzb9VBuZn/SSfE/dHG2DJI7EUVTyTQ85zGPNPXjgsfG5Tw2PMLcAjLkSrN0V0fPMLmJiSX3E=,iv:xb7oq4AXtslWxhJlAp4ebbFh2JeJvYnplSOOORb3z84=,tag:GdbGKCIpgfOwVqtECxEdcg==,type:str]", "unencrypted_suffix": "_unencrypted", "version": "3.10.2" } -- 2.54.0