sonarr, radarr, lidarr provide their API; ombi reaches them through the mesh #156
Open
mesh-admin
wants to merge 7 commits from
feat/servarr-api-provision into main
pull from: feat/servarr-api-provision
merge into: :main
:main
:feat/qbittorrent-for-ace
:feat/servarr-api-provision
:feat/home-assistant-for-ace
:feat/tautulli-for-ace
:feat/bookshelf-for-ace
:feat/lidarr-for-ace
:feat/radarr-for-ace
:feat/sonarr-for-ace
:feat/jackett-for-ace
:feat/oidc-client-provision
:feat/mosquitto-placed
:feat/nodered-for-ace
:feat/influxdb-for-ace
:feat/kometa-for-ace
:feat/plex-for-ace
:fix/manifests-publish-software-ports
:feat/n8n-for-ace
:feat/letta-for-ace
:feat/baserow-for-ace
:feat/supabase-for-ace
:feat/nzbget-for-ace
:feat/matrix-for-ace
:feat/bazarr-for-ace
:feat/redis-for-ace
:feat/mssql-for-ace
:fix/sidecars-dial-the-port-they-were-given
:feat/grafana-for-ace
:feat/unifi-for-ace
:feat/icecast-for-ace
:feat/ombi-for-ace
:chore/remove-the-network-checker-module
:feat/a-network-checker-module
:feat/modules-name-their-endpoints
:fix/a-routed-module-listens-from-the-mesh
:fix/the-resolver-declares-both-protocols
:fix/sshd-declares-the-daemon-it-owns
:fix/fail2ban-bans-through-what-every-machine-has
:fix/fail2ban-declares-the-log-its-own-jail-reads
:fix/fail2ban-restarts-on-its-log-target
:fix/fail2ban-declares-where-it-logs
:feat/the-catalogue-hears-what-it-missed
:feat/the-catalogue-prepares-its-own-schema
:fix/the-catalogue-declares-the-event-it-emits
:feat/a-merge-rebuilds-what-it-changed
:fix/a-merge-older-than-the-watching-is-history
:fix/a-merge-announced-is-said
:fix/the-forge-watches-every-repository
:feat/the-forge-announces-every-merge
:feat/nats-serves-the-meshs-certificate
:fix/nats-declares-its-base
:feat/amqp-leaves-the-catalogue
:restore/broker-claim
:revert/broker-seat-claim
:fix/broker-seat-must-stay-held
:fix/go-126-base
:feat/nats-genesis
:feat/ssh-client-module
Reference in New Issue
Block a user
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.
ombi reaches Sonarr, Radarr and Lidarr through the mesh instead of by container name on HAL's
proxynetwork, which the mesh does not have. Development and tests only — nothing is assigned or deployed.Includes two unmerged branches
origin/feat/ombi-for-ace(PR #145) — ombi's converted manifest. This PR builds on it.origin/fix/sidecars-dial-the-port-they-were-given(PR #154) — the sonarr/radarr/lidarr sidecars dial${port:…}.Merge those two first; this diff then shrinks to the two commits below.
The provision: one per app, not one
servarr-apisonarr-api{scheme: http, port: 8989, url-base: ""}at,port(the machine's published port),scheme,url-base,as,fromradarr-api{scheme: http, port: 7878, url-base: ""}lidarr-api{scheme: http, port: 8686, url-base: ""}The pair credential, delivered as the file under
secrets.<app>-api, is the app's API key.Why not one
servarr-apiwith three providers. The controller cannot express one consumer requiring one name from several providers:requiresis a list of names, and a requirement is matched by name everywhere. ADR 0094 rejected "require the provision several times" for exactly this reason.Pinnedis per node per provision (resolve.go). So one name gets one provider for every consumer on the node.ADR 0027 points the same way. ombi's code is coupled to each app's own API: Sonarr and Radarr speak v3, Lidarr speaks v1, and ombi keeps a different settings document for each. A per-app name is the honest contract.
No
grantsand no provisioner on the providers. A Servarr instance has exactly one API key, so a per-consumer credential cannot be minted into it.Rejected alternative: the provider's sidecar writes a minted key into the app. Servarr would take one (
<ApiKey>in config.xml, orSONARR__AUTH__APIKEY). But the mesh mints one credential per consumer pair. With two consumers (ombi and, later, jackett or bazarr) there are two different values and only one slot. Writing them would also replace the key every existing client already uses.ombi: a run-once step that writes into ombi's database through ombi's API
ombi keeps its Servarr connections in
OmbiSettings.db, not in a file. The newservarrcontainer isrun-once, runs on the host network and runs/app/modules/ombi/dist/servarr/index.js. It follows route-adapter's step pattern. For each app it:/api/v3/system/status, or/api/v1/system/statusfor Lidarr).GET /api/v1/Settings/<app>) and compares onlyip,port,ssl,subDirandapiKey.POSTs those fields only when they differ. Quality profiles, root folders, language profiles, tags andenabledare kept as they are. Radarr's settings document is{radarr, radarr4K}; onlyradarris touched.POST /api/v1/Tester/<app>).api-keysecret and the six mesh files.restart-onnames all six (bound-*,secret-*), so the step runs again when a provider moves or a credential is accepted (ADR 0099).at(a machine off the private network) is refused: from ombi's container, loopback is ombi itself.When the pair credential has not been accepted yet
SecretFormints a random value for any pair that has none (internal/inventory/secrets.go), and no Servarr app will ever accept it. The step finds this in step 2 (a 401), writes nothing, keeps ombi's working key, and fails with:Migration on ace (operator steps)
sonarr,radarrandlidarrmust be assigned somewhere before ombi can be assigned. With none of them, the resolver refuses:nothing in this mesh provides "sonarr-api", wanted by ombi — assign sonarr to a node.<ApiKey>from its config.xml (/services/<app>/config/config.xmltoday):secret accept ace ombi sonarr-api --provider ace --from <sonarr ApiKey file>secret accept ace ombi radarr-api --provider ace --from <radarr ApiKey file>secret accept ace ombi lidarr-api --provider ace --from <lidarr ApiKey file>secret accept ace ombi api-key, as in PR #145.Tested
MESH_CATALOGUE=<this branch> go test -count=1 ./internal/catalogue/ ./internal/inventory/in mesh-controller passes. The catalogue parse test reports 73 manifests and is not skipped.Resolution scratch tests against this branch's real manifests (not committed to mesh-controller). On one node with sonarr, radarr, lidarr, ombi and route-adapter:
For: ombi.at: ace.internaland the machine-side port (20101/20102/20103 given).${dir:state}/<app>-api.secret.restart-onrenamed toombi.bound-*andombi.secret-*, each naming a resource in the declaration.${…}, and the step ordered afterombi.serverandombi.runtime.Resolution with providers on another node resolves. With none, the refusal names all three. With two sonarrs, it is refused until pinned, and the pin is honoured.
npm run typecheck,npm run build(the Dockerfile's tsc line, non-strict) andnpm testpass: 9 tests against fake ombi and fake apps.End to end with throwaway containers (catalogue-pinned sonarr, radarr, lidarr and ombi digests, fresh scratch config, private docker network), with ombi preloaded with HAL-style settings (
ip: sonarr,qualityProfile: 3, a 4K radarr, …):wrote ip, apiKey; connection testedfor each, exit 0. OnlyipandapiKeychanged (plus theidombi assigns on its first save). Radarr 4K was untouched. ombi's Tester returnsisValid: truefor all three, and ombi lists Sonarr's quality profiles through the new connection.already as the mesh says, no writes, exit 0.Everything was removed afterwards.
Open questions / model gaps
accepted-only; the plan refuses the pair until it is accepted, andsecret acceptfor such a pair can be given once per provider and fanned out to its consumer pairs.grants, the provider's sidecar (which already reads config.xml) could check each consumer's delivered pair credential against the app's real key and report a mismatch on the provider too. Not built.servedOnThisMachinelays the provider module's whole settings layer overserves. Aurl-basesetting on sonarr reaches consumers, which is intended, but so would any other setting key. Worth a look in the controller.jackett-torznab, where jackett has one API key, so the same accepted pattern applies) and bazarr / home-assistant →sonarr-api/radarr-apiconsumers with their own write-in steps.ombi's definition named /services/ombi/config (a HAL machine path) in three places and pinned an image older than the one ace runs. Ombi migrates its own SQLite schema, so a take onto the older pin (v4.53.10-ls267) would start it on a database the newer build (ls269) already touched. - config is a pathless placed directory, mounted as ${dir:config} - a state directory placed at the assignment root carries route.json - image pinned to the digest ace runs today (v4.53.10-ls269) - the sidecar reaches ombi on the machine port the mesh assigns (${port:3579}) rather than assuming 3579 is free - the sidecar no longer mounts ombi's data directory: MESH_OMBI_CONFIG_DIR is read by no code, and the mount exposed the databases for nothing Verified: catalogue tests (MESH_CATALOGUE set, 6 pass, none skipped); the pinned image starts as PUID 1000 in a 0700 dir and answers /api/v1/Status 200; data owned 1001:2000 (ace's media ids) under a 1000:1000 dir is re-owned by the image's init and serves 200; a minted ApiKey is refused (401) - the api-key secret must be accepted from ombi's own settings.A host-network sidecar reaches its service over the machine's loopback, and the mesh publishes that service on a machine port it assigns (ADR 0038) — so dialling the software's port reaches whatever else holds it. On ace, searxng's sidecar dialled 127.0.0.1:8080 and got unifi's inform port. The same shape in bazarr, bookshelf, lidarr, nzbget, qbittorrent, radarr and sonarr; each now asks with ${port:N} (hq 088). Found in review of ace's module preparation.Review — approve
sonarr-api,radarr-api,lidarr-api) are forced by one-provider-per-name-per-node resolution; ADR 0027 agrees (v3 vs v1 APIs, separate ombi settings documents).secret acceptcommand — writing nothing, keeping ombi's working key — is the right failure.ace.internal:<machine port>while ace is adopted (traefik → searxng does, 200).Merge order: #145 and #154 first. The proposed follow-ups (accept-only pair credentials, one accept fanning out to all pairs, optional requirements) are worth hq issues.
ombi now reaches plex through
plex-api, in the same step as the Servarr apps (commitsb068a9d,0010b6b)servarrtoconnections(mesh-ombi-connections,connections/index.ts), because it now covers plex too.servarr/settings.tsis unchanged apart from exportingombiCallandisLoopback. Every HTTP call is now bounded to 20 s.plex/settings.ts:secret accept ace ombi plex-api --provider ace --from <file>./identity, and writes only ip, port, ssl, subDir and plexAuthToken, only when they differ.enable, watchlist import and batch size are never touched, and neither is any other server's entry.POST /Tester/plex.7656…, from an older server), while its address,plex.zurag.be:443, answers as today's plex (a728…). When no entry carries the identifier, the step asks each entry's own address for/identity. An entry plex answers for is adopted: it gets the bound connection plus the corrected identifier, and keeps its name and its 6 libraries. Entries that are unreachable or answer as another server are left alone.go test ./internal/catalogue/passes.enableand the batch size.View command line instructions
Checkout
From your project repository, check out a new branch and test the changes.