40 Commits
Author SHA1 Message Date
mesh-admin b2a7fbfd11 Merge pull request 'bazarr, tautulli: read the app's own API key through its container' (#14) from fix/bazarr-tautulli-read-config-through-their-container into main 2026-10-03 23:10:06 +00:00
jochen 3fbe40c3ec bazarr, tautulli: read the app's own key through its container
Moved out of their containers, their code runs as the node runtime's account, which may not read
the app's config directory: it belongs to the account the app runs as on that machine, mode 0700.
Read the file directly when allowed, else through the app's own container, which owns it.
2026-10-04 01:09:54 +02:00
mesh-admin e94c3ac1a1 Merge pull request 'Twelve media modules' code moves into bundles the node's runtime serves (hq ADR 0198, to-be 38 WP4c)' (#13) from feat/0198-waves-2-3-module-code-moves into main 2026-10-03 23:01:18 +00:00
jochen a8ace3e528 tautulli: the runtime serves its watcher and tools, and its plex step is a run-once process (hq ADR 0198)
The mesh-tautulli container and the mesh-tautulli-plex step container go with the Dockerfile, build bases and bus credential. The step runs node on the bundle and reads the plex binding where the mesh writes it; it still runs again when the server or the binding changes.
2026-10-04 00:45:22 +02:00
jochen 9234ba0e94 ombi: the runtime serves its watcher and tools, and its connections step is a run-once process (hq ADR 0198)
The mesh-ombi container and the mesh-ombi-connections step container go with the Dockerfile, build bases and bus credential. The step runs node on the bundle and reads the servarr and plex bindings and the API key where the mesh writes them; it still runs again when one changes.
2026-10-04 00:45:22 +02:00
jochen 9db000a07f bazarr: the runtime serves its watcher and tools, and its servarr step is a run-once process (hq ADR 0198)
The mesh-bazarr container and the mesh-bazarr-servarr step container go with the Dockerfile, build bases, bus credential and state directory. The step runs node on the bundle and reads the sonarr and radarr bindings where the mesh writes them; it still runs again when one changes.
2026-10-04 00:45:22 +02:00
jochen ed4f54dff9 bookshelf: the runtime serves its watcher and tools, and its downloads step is a run-once process (hq ADR 0198)
The mesh-bookshelf container and the mesh-bookshelf-downloads step container go with the Dockerfile, build bases and bus credential. The step runs node on the bundle with its words in the process's env, the bound clients' files, the app's config.xml and its memory read where the mesh writes them; it still runs again when a binding it reads changes.
2026-10-04 00:45:22 +02:00
jochen b9fd0cfe81 lidarr: the runtime serves its watcher and tools, and its downloads step is a run-once process (hq ADR 0198)
The mesh-lidarr container and the mesh-lidarr-downloads step container go with the Dockerfile, build bases and bus credential. The step runs node on the bundle with its words in the process's env, the bound clients' files, the app's config.xml and its memory read where the mesh writes them; it still runs again when a binding it reads changes.
2026-10-04 00:45:22 +02:00
jochen 36209b264d radarr: the runtime serves its watcher and tools, and its downloads step is a run-once process (hq ADR 0198)
The mesh-radarr container and the mesh-radarr-downloads step container go with the Dockerfile, build bases and bus credential. The step runs node on the bundle with its words in the process's env, the bound clients' files, the app's config.xml and its memory read where the mesh writes them; it still runs again when a binding it reads changes.
2026-10-04 00:45:22 +02:00
jochen f4d8faead0 sonarr: the runtime serves its watcher and tools, and its downloads step is a run-once process (hq ADR 0198)
The mesh-sonarr container and the mesh-sonarr-downloads step container go with the Dockerfile, build bases and bus credential. The step runs node on the bundle with its words in the process's env, the bound clients' files, the app's config.xml and its memory read where the mesh writes them; it still runs again when a binding it reads changes.
2026-10-04 00:45:22 +02:00
jochen 8fbed91540 qbittorrent: its watcher and tools run in the node's runtime (hq ADR 0198)
The mesh-qbittorrent container goes with its Dockerfile, build bases and bus credential; its env becomes the bundle's words with mount targets folded back to host paths.
2026-10-04 00:41:51 +02:00
jochen 995f2b69ba nzbget: its watcher and tools run in the node's runtime (hq ADR 0198)
The mesh-nzbget container goes with its Dockerfile, build bases and bus credential; its env becomes the bundle's words with mount targets folded back to host paths.
2026-10-04 00:41:51 +02:00
jochen 4d94da04d4 kometa: its tools run in the node's runtime (hq ADR 0198)
The mesh-kometa container goes with its Dockerfile, build bases and bus credential; its env becomes the bundle's words with mount targets folded back to host paths.
2026-10-04 00:41:51 +02:00
jochen 2f6f14522e jackett: its tools run in the node's runtime (hq ADR 0198)
The mesh-jackett container goes with its Dockerfile, build bases and bus credential; its env becomes the bundle's words with mount targets folded back to host paths.
2026-10-04 00:41:51 +02:00
jochen 7fee32ecbf plex: its handlers and tools run in the node's runtime (hq ADR 0198)
The mesh-plex container goes with its Dockerfile, build bases, bus credential and state directory. The token is still read from the server's own Preferences.xml, now under the config directory where the mesh places it: the code looked for it beneath a data directory that was only the container's layout, and a manifest cannot name a placed directory's parent, so the client takes the config directory itself (MESH_PLEX_CONFIG_DIR), keeping the old word for a hand run.
2026-10-04 00:40:08 +02:00
mesh-admin 5eee14ee0a Merge pull request 'A dead tracker is not the machine's failure (hq ADR 0187)' (#12) from fix/a-dead-tracker-is-not-the-meshs-failure into main 2026-10-02 18:41:55 +00:00
jschoubben 1a2f48492d A dead tracker is not the machine's failure (hq ADR 0187)
The step already left a found feed as found when the app refused to save it. An app can also save
it and then fail its own test — saving validates settings, the test runs a live search — and that
shape failed the whole apply. One dead public tracker stopped the home server converging for six
hours, and a clean apply gates the found firewall's retirement, so a tracker was holding a firewall
record hostage. What decides is whose entry it is: one the mesh only found is a notice, one the
operator listed is still a failure. All four copies of the step, and a test with the machine's own
message that models the app's two validations apart.
2026-10-02 20:41:29 +02:00
mesh-admin 96fa0c8e60 Merge pull request 'qbittorrent: the listens' why texts no longer spell a port placeholder' (#11) from fix/qbittorrent-why-without-placeholders into main 2026-10-01 19:43:57 +00:00
jschoubben 208aa79ad1 qbittorrent: the listens' why texts no longer spell a port placeholder
The mesh's filter is composed from every module's listens, why text included,
and the renderer fills every ${port:N} it finds in a written file — refusing
one the writing module did not declare. Two why texts quoted the env lines
WEBUI_PORT=${port:8112} and TORRENTING_PORT=${port:6881} literally, so the
filter file on a converging node named qbittorrent's ports as nftables' own:
"nftables has a file that says ${port:6881}, and nftables does not say it
listens on 6881" (ace, 2026-10-01). Said in words instead.
2026-10-01 21:43:51 +02:00
mesh-admin 7c5beae716 Merge pull request 'downloads: an adopted feed the app refuses is said and left as found, not a failure' (#10) from fix/adopted-dead-feed-is-a-notice into main 2026-10-01 15:35:37 +00:00
jschoubben 3020530ba8 downloads: an adopted feed the app refuses is said and left as found, not a failure
The step adopts the jackett feeds it finds in radarr/sonarr/lidarr/bookshelf
and re-points them at the mesh's jackett. Two of radarr's (KAT, Torlock) are
dead at the source, radarr refuses to save them, and the step exited 1 on
every apply — the node reported wrong, each heartbeat, about a public tracker
nothing in the mesh can repair. Such an entry is now a notice ("left as
found", with radarr's words and the two remedies); an indexer the settings
list is still a failure, as the mesh was told to make it.
2026-10-01 17:35:31 +02:00
mesh-admin b64f5a14ad Merge pull request 'radarr, sonarr, lidarr: a search tells everything the app keeps, and where the item is in Plex' (#9) from feat/arr-search-tells-all-and-plex into main 2026-10-01 13:54:51 +00:00
jschoubben 9ffd0dd96a radarr, sonarr, lidarr: a search tells everything the app keeps, and where the item is in Plex
The library search returned title, year, status and monitored. It now
returns every field the app keeps (file on disk, quality, size, path,
counts, ratings, genres, added…), and each module binds plex-api so the
first ten hits carry Plex's view: rating key, library, resolution, added,
watched, and a link that opens the item in Plex.
2026-10-01 15:54:20 +02:00
mesh-admin 6282a0ed87 Merge pull request 'arr add: a root folder must be named when several exist; lidarr's lookup shows the artist's name' (#8) from fix/arr-add-defaults into main 2026-10-01 12:57:59 +00:00
jschoubben 702e9f8419 arr add: a root folder must be named when several exist; lidarr's lookup shows the artist's name
Sonarr's first root folder is /anime, so an unnamed add landed a series
there. A profile is a preference and the first stands in; a root folder
decides where data lands, so with several it is named or the refusal
lists them. Lidarr's lookup used title where the field is artistName.
2026-10-01 14:57:49 +02:00
mesh-admin da815c0238 Merge pull request 'radarr, sonarr, lidarr: control tools — look up, add and remove' (#7) from feat/arr-control-tools into main 2026-10-01 12:26:27 +00:00
jschoubben 26a8ae3b88 radarr, sonarr, lidarr: control tools — look up, add and remove
Each gains _lookup (the metadata source, by title or by id), _add (by TMDb,
TVDb or MusicBrainz id, with the first quality/metadata profile and root
folder unless named, monitored, searching only when asked) and _remove (by
the app's id, files kept unless asked).
2026-10-01 14:26:17 +02:00
mesh-admin 1fff041260 Merge pull request 'qbittorrent: the init writes the WebUI login in python, not awk' (#6) from fix/qbittorrent-init-writes-the-login-in-python into main 2026-10-01 11:50:33 +00:00
jschoubben 5be701b78e qbittorrent: the init writes the WebUI login in python, not awk
The keys carry backslashes (WebUI\Username); awk -v escape-processes them,
so the edit matched nothing and the vault's credential never reached the
software. Python edits the file literally and also sets BanDuration=60.
2026-10-01 13:50:23 +02:00
mesh-admin 7699621635 Merge pull request 'Sidecars read the vault's key and restart when it is remade; a stale WebUI attempt bans for a minute' (#5) from fix/sidecars-read-the-vaults-key into main 2026-10-01 11:36:08 +00:00
jschoubben 8143811c84 Sidecars read the vault's key and restart when it is remade; a stale WebUI attempt bans for a minute
The jackett, sonarr, radarr and lidarr runtimes discovered the key from
the software's config at start, so after a rotation they held the old one.
They now read the vault's file first and restart when it is remade.
qBittorrent's init sets WebUI\BanDuration=60 so a consumer's step still
carrying the previous value no longer locks the others out for an hour.
2026-10-01 13:35:45 +02:00
mesh-admin 7c97cb7a02 Merge pull request 'The six media providers share their one credential with every consumer (ADR 0158)' (#4) from feat/0158-one-credential-shared-with-every-consumer into main 2026-10-01 11:05:14 +00:00
jschoubben 11da93e966 The six media providers share their one credential with every consumer (ADR 0158)
nzbget, qbittorrent, jackett, sonarr, radarr and lidarr each hold one
credential; the offer now names the provider's own secret as the
provision's credential, that secret is taken at start, and a
custom-cont-init script applies the file to the software on every start
(nzbget: NZBGET_USER beside the password it already took as a start
option; qbittorrent: the WebUI login's PBKDF2 hash; jackett: APIKey in
ServerConfig.json; the arrs: <ApiKey> in config.xml). Nothing is accepted
per consumer any more; rotating the provider's secret reaches everyone.
2026-10-01 13:05:03 +02:00
mesh-admin 1f0e9b2c89 Merge pull request 'nzbget, qbittorrent: the client reads the username the manifest's settings define' (#3) from fix/clients-read-the-username-setting into main 2026-09-30 22:43:50 +00:00
jschoubben 27d435e7aa nzbget, qbittorrent: the client reads the username the manifest's settings define
The runtime config carries `username` (the manifest's default, the
assignment's value); the clients looked for `user`, fell through to the
software's config file — empty at the sidecar's start on a fresh
placement — and defaulted to the software's own login. On ace the tools
answered 401 while the server itself accepted the same credentials.
2026-10-01 00:43:39 +02:00
mesh-admin cb7f7f257c Merge pull request 'The identity a module runs as is the assignment's, not the definition's' (#2) from feat/run-identity-is-the-assignments into main 2026-09-30 22:36:03 +00:00
jschoubben 52abeb34bd The identity a module runs as is the assignment's, not the definition's
PUID/PGID (PLEX_UID/PLEX_GID for plex) were 1000:1000 in every definition —
one machine's fact written where it is true of no other (ADR 0112). Each
module now renders identity.env from ${setting:puid} and ${setting:pgid};
the mesh-wide layer carries the image's default, a node whose data belongs
to somebody else says so. An adopted machine's library must never be
re-owned (ace: 1001:2000, hq 153).
2026-10-01 00:35:07 +02:00
mesh-admin e4c78b67e1 Merge pull request 'The media catalogue places the mesh's files and names its accesses (issues 174, 153)' (#1) from feat/153-174-the-media-catalogue-places-and-names into main 2026-09-30 22:27:40 +00:00
jschoubben c1ebc5a961 The media catalogue places the mesh's files and names its accesses (issues 174, 153)
Twelve definitions stop naming /var/lib/mesh/<module>: the directory says `place: "mesh"` (kometa
gains the directory it never declared), and every credential and mount names it as
${dir:mesh-state}. Every access has an id, kept beside its path as the default an assignment may
replace, and the host side of every mount says ${access:<id>} — so a home server's assignment can
say `accesses: {series: "/storage/media/series", …}` and `places: {config: {path: …, owner: …}}`
and the mounts follow. Resolved with no placement, eleven converted definitions name exactly the
paths they named before (TestPlacedDirectoriesKeepTheirPaths over both checkouts); kometa's added
directory is where the mesh already writes.

Needs the controller from mesh-controller #175/#176, running since 2026-10-01 00:06.
2026-10-01 00:27:27 +02:00
jschoubben db3adfa4c6 The media chain as mesh modules, in one catalogue
sonarr, radarr, lidarr, bazarr, nzbget, qbittorrent, jackett, bookshelf,
plex, tautulli, kometa and ombi, taken from the novox/mesh-catalog branches
that prepared them for ace (PRs 145-168), consolidated in stack order.
kometa gains a minimal runtime sidecar (kometa_status, kometa_config) and
declares its tmdb key as an own secret instead of a "secret" requirement.
2026-09-30 21:32:59 +02:00