From 69c8a5edf81a286ab86967317ca6b5a2fbaa5262 Mon Sep 17 00:00:00 2001 From: jochen Date: Sat, 5 Sep 2026 22:14:33 +0200 Subject: [PATCH] media stack: the shared library is accessed, not owned MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 04-ISSUES/036: eight media modules each declared the shared library and download directories under /services/media/* as their own `directory` resources. Six of them owning one path is the collision the resolver refuses — so the stack's only sensible assignment, all on one machine sharing one filesystem, would be refused the first time two landed together. The media library is the operator's, owned by no module (novox/hq ADR 0051). Move every /services/media/* path from an owned `directory` resource to an `accesses` entry: the mesh mounts it and owns nothing — does not create, chown, reconcile or remove it — and several modules may access one path with no conflict. Each module's own config and mesh-state directories stay owned resources. Modes are least-privilege: plex reads the libraries it streams; the managers and download clients get read-write on what they import and write; bazarr writes subtitles into the libraries (read-write) and only reads the download spool. The container volume mounts are unchanged. Claude-Session: https://claude.ai/code/session_01LrgweAeERJYBg88c5cKDzF --- modules/bazarr/module.json | 46 +++++++++++--------------- modules/bookshelf/module.json | 24 ++++++-------- modules/lidarr/module.json | 24 ++++++-------- modules/nzbget/module.json | 13 ++++---- modules/plex/module.json | 57 +++++++++++++-------------------- modules/qbittorrent/module.json | 13 ++++---- modules/radarr/module.json | 24 ++++++-------- modules/sonarr/module.json | 35 ++++++++------------ 8 files changed, 96 insertions(+), 140 deletions(-) diff --git a/modules/bazarr/module.json b/modules/bazarr/module.json index 724f0e3..9a4f684 100644 --- a/modules/bazarr/module.json +++ b/modules/bazarr/module.json @@ -18,6 +18,24 @@ "why": "managing subtitles" } ], + "accesses": [ + { + "path": "/services/media/movies", + "mode": "read-write" + }, + { + "path": "/services/media/series", + "mode": "read-write" + }, + { + "path": "/services/media/anime", + "mode": "read-write" + }, + { + "path": "/services/media/downloads", + "mode": "read" + } + ], "resources": [ { "id": "mesh-state", @@ -32,34 +50,6 @@ "mode": "0700", "owner": "1000:1000" }, - { - "id": "media-movies", - "type": "directory", - "path": "/services/media/movies", - "mode": "0755", - "owner": "1000:1000" - }, - { - "id": "media-series", - "type": "directory", - "path": "/services/media/series", - "mode": "0755", - "owner": "1000:1000" - }, - { - "id": "media-anime", - "type": "directory", - "path": "/services/media/anime", - "mode": "0755", - "owner": "1000:1000" - }, - { - "id": "media-downloads", - "type": "directory", - "path": "/services/media/downloads", - "mode": "0755", - "owner": "1000:1000" - }, { "id": "server", "type": "container", diff --git a/modules/bookshelf/module.json b/modules/bookshelf/module.json index 6242c72..af52d4d 100644 --- a/modules/bookshelf/module.json +++ b/modules/bookshelf/module.json @@ -20,6 +20,16 @@ "why": "managing the ebook/audiobook library" } ], + "accesses": [ + { + "path": "/services/media/books", + "mode": "read-write" + }, + { + "path": "/services/media/downloads", + "mode": "read-write" + } + ], "resources": [ { "id": "mesh-state", @@ -34,20 +44,6 @@ "mode": "0700", "owner": "1000:1000" }, - { - "id": "media-books", - "type": "directory", - "path": "/services/media/books", - "mode": "0755", - "owner": "1000:1000" - }, - { - "id": "media-downloads", - "type": "directory", - "path": "/services/media/downloads", - "mode": "0755", - "owner": "1000:1000" - }, { "id": "server", "type": "container", diff --git a/modules/lidarr/module.json b/modules/lidarr/module.json index 5172bba..c285762 100644 --- a/modules/lidarr/module.json +++ b/modules/lidarr/module.json @@ -20,6 +20,16 @@ "why": "managing music" } ], + "accesses": [ + { + "path": "/services/media/music", + "mode": "read-write" + }, + { + "path": "/services/media/downloads", + "mode": "read-write" + } + ], "resources": [ { "id": "mesh-state", @@ -34,20 +44,6 @@ "mode": "0700", "owner": "1000:1000" }, - { - "id": "media-music", - "type": "directory", - "path": "/services/media/music", - "mode": "0755", - "owner": "1000:1000" - }, - { - "id": "media-downloads", - "type": "directory", - "path": "/services/media/downloads", - "mode": "0755", - "owner": "1000:1000" - }, { "id": "server", "type": "container", diff --git a/modules/nzbget/module.json b/modules/nzbget/module.json index 2c0b92d..e3dee8e 100644 --- a/modules/nzbget/module.json +++ b/modules/nzbget/module.json @@ -20,6 +20,12 @@ "why": "the download client's pages" } ], + "accesses": [ + { + "path": "/services/media/downloads", + "mode": "read-write" + } + ], "resources": [ { "id": "mesh-state", @@ -34,13 +40,6 @@ "mode": "0700", "owner": "1000:1000" }, - { - "id": "media-downloads", - "type": "directory", - "path": "/services/media/downloads", - "mode": "0755", - "owner": "1000:1000" - }, { "id": "server", "type": "container", diff --git a/modules/plex/module.json b/modules/plex/module.json index bb9dc37..665252f 100644 --- a/modules/plex/module.json +++ b/modules/plex/module.json @@ -23,6 +23,28 @@ "why": "streaming and the app; reaching it from outside is a route grant later" } ], + "accesses": [ + { + "path": "/services/media/movies", + "mode": "read" + }, + { + "path": "/services/media/series", + "mode": "read" + }, + { + "path": "/services/media/anime", + "mode": "read" + }, + { + "path": "/services/media/music", + "mode": "read" + }, + { + "path": "/services/media/audiobooks", + "mode": "read" + } + ], "resources": [ { "id": "mesh-state", @@ -44,41 +66,6 @@ "mode": "0700", "owner": "1000:1000" }, - { - "id": "media-movies", - "type": "directory", - "path": "/services/media/movies", - "mode": "0755", - "owner": "1000:1000" - }, - { - "id": "media-series", - "type": "directory", - "path": "/services/media/series", - "mode": "0755", - "owner": "1000:1000" - }, - { - "id": "media-anime", - "type": "directory", - "path": "/services/media/anime", - "mode": "0755", - "owner": "1000:1000" - }, - { - "id": "media-music", - "type": "directory", - "path": "/services/media/music", - "mode": "0755", - "owner": "1000:1000" - }, - { - "id": "media-audiobooks", - "type": "directory", - "path": "/services/media/audiobooks", - "mode": "0755", - "owner": "1000:1000" - }, { "id": "server", "type": "container", diff --git a/modules/qbittorrent/module.json b/modules/qbittorrent/module.json index daa6629..56b0858 100644 --- a/modules/qbittorrent/module.json +++ b/modules/qbittorrent/module.json @@ -20,6 +20,12 @@ "why": "the download client's pages" } ], + "accesses": [ + { + "path": "/services/media/downloads", + "mode": "read-write" + } + ], "resources": [ { "id": "mesh-state", @@ -34,13 +40,6 @@ "mode": "0700", "owner": "1000:1000" }, - { - "id": "media-downloads", - "type": "directory", - "path": "/services/media/downloads", - "mode": "0755", - "owner": "1000:1000" - }, { "id": "server", "type": "container", diff --git a/modules/radarr/module.json b/modules/radarr/module.json index 607972f..2d09bfe 100644 --- a/modules/radarr/module.json +++ b/modules/radarr/module.json @@ -20,6 +20,16 @@ "why": "managing films" } ], + "accesses": [ + { + "path": "/services/media/movies", + "mode": "read-write" + }, + { + "path": "/services/media/downloads", + "mode": "read-write" + } + ], "resources": [ { "id": "mesh-state", @@ -34,20 +44,6 @@ "mode": "0700", "owner": "1000:1000" }, - { - "id": "media-movies", - "type": "directory", - "path": "/services/media/movies", - "mode": "0755", - "owner": "1000:1000" - }, - { - "id": "media-downloads", - "type": "directory", - "path": "/services/media/downloads", - "mode": "0755", - "owner": "1000:1000" - }, { "id": "server", "type": "container", diff --git a/modules/sonarr/module.json b/modules/sonarr/module.json index 9e19164..ace8073 100644 --- a/modules/sonarr/module.json +++ b/modules/sonarr/module.json @@ -20,6 +20,20 @@ "why": "managing series" } ], + "accesses": [ + { + "path": "/services/media/series", + "mode": "read-write" + }, + { + "path": "/services/media/anime", + "mode": "read-write" + }, + { + "path": "/services/media/downloads", + "mode": "read-write" + } + ], "resources": [ { "id": "mesh-state", @@ -34,27 +48,6 @@ "mode": "0700", "owner": "1000:1000" }, - { - "id": "media-series", - "type": "directory", - "path": "/services/media/series", - "mode": "0755", - "owner": "1000:1000" - }, - { - "id": "media-anime", - "type": "directory", - "path": "/services/media/anime", - "mode": "0755", - "owner": "1000:1000" - }, - { - "id": "media-downloads", - "type": "directory", - "path": "/services/media/downloads", - "mode": "0755", - "owner": "1000:1000" - }, { "id": "server", "type": "container",