The metadata-provider keys Jellyfin needs now reach the container-owned plugin XML through one host file the deployer installs — never through git, never through argv.
scripts/jellyfin-providers.sh plan|install|verify — the only thing that writes provider configuration. It reads /home/dank/Containers/jellyfin/provider-keys (TMDB_API_KEY=, TVDB_API_KEY=, optional TVDB_PIN=, mode 600 — the fleet's per-service env-file convention, same shape as grafana/caddy/pihole/copyparty/romm/seerr), prints no key byte, and refuses a keys file that is group- or world-readable or has no line for the provider about to be written.
install is idempotent: it never touches a key the UI already saved, moves a copy of every XML it edits into ~/.AGENTTRASH/<date>-jellyfin-providers/ first, and installs the TVDB plugin only when the pinned official zip carries the plugin guid.
TVDB (thetvdb_24.0.0.0, targetAbi 12.0.0.0) is pinned three ways — sha256, the official plugin guid, and version. A sha match alone only proves the bytes are the bytes fetched. Rollback is one move to trash plus the pin revert; a second thetvdb_* directory is a refusal, not a quiet second copy.
scripts/deploy-remote.sh gains ensure_provider_keys, run before the jellyfin restart: the plugin and its keys are read at start-up, so a restart-first deploy boots the old state. An absent keys file WARNs rather than failing — a host without keys still deploys, degraded rather than broken.
scripts/deploy-plan.py routes scripts/jellyfin-providers.sh to the jellyfin service.
tests/jellyfin_providers.bats — 20 behavior tests: the refusals (mode, missing line, missing plugin, sha/guid/version mismatch, foreign plugin dir), idempotence, a UI-saved key kept, a pin landing as <Pin>, no key bytes in any output, no plugin XML tracked in git, the deployer ordering, the planner routing.
Docs: the Jellyfin page owns the key contract (which providers are core in 12, the exact XML, the 600 rule, the pins, the OMDb refusal and its reason); index.md carries the same locator.
Why
Jellyfin 12 ships TMDb, StudioImages and MusicBrainz as core plugins — no DLL, just a configuration file under the data root that the running container owns. So enabling TVDB and keying TMDb is a credentials-delivery problem, and the fleet's law is that credentials never enter the checkout. The installer keeps its output to actions and key lengths so the whole flow is safe to run in a shared transcript; the key value never enters the shell environment or argv either, because /proc/<pid>/cmdline is world-readable.
Verification
./tests/run.sh — ALL TESTS PASSED (full suite, 665 @test cases across the seams), including the new jellyfin_providers seam and docs_completeness against the rewritten page and index row.
Host side lands after merge: place provider-keys, then ./scripts/deploy.sh dankserver --service jellyfin.
Deliberately not in this PR
TVDB is installed and keyed, not enabled as a library metadata agent. TMDb stays the identifier; flipping a library's providers re-fetches metadata across the whole library, which is Frank's call, not a side effect of a key deploy.
OMDb stays unconfigured: key issued by email, 1 000 requests/day, and a ToS that forbids storing the returned data.
## What
The metadata-provider keys Jellyfin needs now reach the container-owned plugin XML through one host file the deployer installs — never through git, never through argv.
- `scripts/jellyfin-providers.sh plan|install|verify` — the only thing that writes provider configuration. It reads `/home/dank/Containers/jellyfin/provider-keys` (`TMDB_API_KEY=`, `TVDB_API_KEY=`, optional `TVDB_PIN=`, mode 600 — the fleet's per-service env-file convention, same shape as grafana/caddy/pihole/copyparty/romm/seerr), prints no key byte, and refuses a keys file that is group- or world-readable or has no line for the provider about to be written.
- `install` is idempotent: it never touches a key the UI already saved, moves a copy of every XML it edits into `~/.AGENTTRASH/<date>-jellyfin-providers/` first, and installs the TVDB plugin only when the pinned official zip carries the plugin guid.
- TVDB (`thetvdb_24.0.0.0`, `targetAbi 12.0.0.0`) is pinned three ways — sha256, the official plugin guid, and version. A sha match alone only proves the bytes are the bytes fetched. Rollback is one move to trash plus the pin revert; a second `thetvdb_*` directory is a refusal, not a quiet second copy.
- `scripts/deploy-remote.sh` gains `ensure_provider_keys`, run **before** the jellyfin restart: the plugin and its keys are read at start-up, so a restart-first deploy boots the old state. An absent keys file WARNs rather than failing — a host without keys still deploys, degraded rather than broken.
- `scripts/deploy-plan.py` routes `scripts/jellyfin-providers.sh` to the `jellyfin` service.
- `tests/jellyfin_providers.bats` — 20 behavior tests: the refusals (mode, missing line, missing plugin, sha/guid/version mismatch, foreign plugin dir), idempotence, a UI-saved key kept, a pin landing as `<Pin>`, no key bytes in any output, no plugin XML tracked in git, the deployer ordering, the planner routing.
- Docs: the Jellyfin page owns the key contract (which providers are core in 12, the exact XML, the 600 rule, the pins, the OMDb refusal and its reason); `index.md` carries the same locator.
## Why
Jellyfin 12 ships TMDb, StudioImages and MusicBrainz as core plugins — no DLL, just a configuration file under the data root that the running container owns. So enabling TVDB and keying TMDb is a credentials-delivery problem, and the fleet's law is that credentials never enter the checkout. The installer keeps its output to actions and key *lengths* so the whole flow is safe to run in a shared transcript; the key value never enters the shell environment or argv either, because `/proc/<pid>/cmdline` is world-readable.
## Verification
`./tests/run.sh` — `ALL TESTS PASSED` (full suite, 665 `@test` cases across the seams), including the new `jellyfin_providers` seam and `docs_completeness` against the rewritten page and index row.
Host side lands after merge: place `provider-keys`, then `./scripts/deploy.sh dankserver --service jellyfin`.
## Deliberately not in this PR
- **TVDB is installed and keyed, not enabled as a library metadata agent.** TMDb stays the identifier; flipping a library's providers re-fetches metadata across the whole library, which is Frank's call, not a side effect of a key deploy.
- **OMDb stays unconfigured**: key issued by email, 1 000 requests/day, and a ToS that forbids storing the returned data.
---
cwd: /home/blank/.omp/wt/wt-20261030-163847-6d1fc1f · session: 01a18f2e-18b1-7573-bcd5-ac99636f8bcb
Jellyfin 12 ships TMDB, TVDB, StudioImages and MusicBrainz as core plugins:
no DLL, only a configuration file under the data root, so the keys have to
reach that XML from somewhere that is not git. They live in the fleet's
per-service env-file convention -- /home/dank/Containers/jellyfin/provider-keys
(TMDB_API_KEY=, TVDB_API_KEY=, optional TVDB_PIN=, mode 600) -- and
scripts/jellyfin-providers.sh plan|install|verify reads only that file, prints
no key byte, and refuses a keys file that is not mode 600 or that has no line
for the provider about to be written. install is idempotent: it never touches
a key the UI already saved, backs up every XML it edits into
~/.AGENTTRASH/<date>-jellyfin-providers/ first, and only installs the TVDB
plugin when the pinned official zip carries the plugin guid.
The deployer runs install BEFORE it restarts jellyfin, because the plugin and
its keys are read at start-up and a restart-first deploy boots the old state.
A missing keys file warns instead of failing: a host without the keys still
deploys, degraded rather than broken. No plugin configuration XML is tracked.
The page carries the deploy-time shape: which four providers are core plugins
in Jellyfin 12, which one is not, the exact XML each key lives in, the host
file the keys come from and its 600 rule, the three-way pin on the TVDB
artifact with its rollback, and what the deployer does when the keys file is
absent. OMDb is recorded as deliberately unconfigured -- keyed by email,
1000 req/day, and a ToS that forbids storing what it returns. index.md's row
carries the same locator.
dank
merged commit 1a1dbac268 into main2026-10-30 22:48:22 -07:00
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.
What
The metadata-provider keys Jellyfin needs now reach the container-owned plugin XML through one host file the deployer installs — never through git, never through argv.
scripts/jellyfin-providers.sh plan|install|verify— the only thing that writes provider configuration. It reads/home/dank/Containers/jellyfin/provider-keys(TMDB_API_KEY=,TVDB_API_KEY=, optionalTVDB_PIN=, mode 600 — the fleet's per-service env-file convention, same shape as grafana/caddy/pihole/copyparty/romm/seerr), prints no key byte, and refuses a keys file that is group- or world-readable or has no line for the provider about to be written.installis idempotent: it never touches a key the UI already saved, moves a copy of every XML it edits into~/.AGENTTRASH/<date>-jellyfin-providers/first, and installs the TVDB plugin only when the pinned official zip carries the plugin guid.thetvdb_24.0.0.0,targetAbi 12.0.0.0) is pinned three ways — sha256, the official plugin guid, and version. A sha match alone only proves the bytes are the bytes fetched. Rollback is one move to trash plus the pin revert; a secondthetvdb_*directory is a refusal, not a quiet second copy.scripts/deploy-remote.shgainsensure_provider_keys, run before the jellyfin restart: the plugin and its keys are read at start-up, so a restart-first deploy boots the old state. An absent keys file WARNs rather than failing — a host without keys still deploys, degraded rather than broken.scripts/deploy-plan.pyroutesscripts/jellyfin-providers.shto thejellyfinservice.tests/jellyfin_providers.bats— 20 behavior tests: the refusals (mode, missing line, missing plugin, sha/guid/version mismatch, foreign plugin dir), idempotence, a UI-saved key kept, a pin landing as<Pin>, no key bytes in any output, no plugin XML tracked in git, the deployer ordering, the planner routing.index.mdcarries the same locator.Why
Jellyfin 12 ships TMDb, StudioImages and MusicBrainz as core plugins — no DLL, just a configuration file under the data root that the running container owns. So enabling TVDB and keying TMDb is a credentials-delivery problem, and the fleet's law is that credentials never enter the checkout. The installer keeps its output to actions and key lengths so the whole flow is safe to run in a shared transcript; the key value never enters the shell environment or argv either, because
/proc/<pid>/cmdlineis world-readable.Verification
./tests/run.sh—ALL TESTS PASSED(full suite, 665@testcases across the seams), including the newjellyfin_providersseam anddocs_completenessagainst the rewritten page and index row.Host side lands after merge: place
provider-keys, then./scripts/deploy.sh dankserver --service jellyfin.Deliberately not in this PR
cwd: /home/blank/.omp/wt/wt-20261030-163847-6d1fc1f · session: 01a18f2e-18b1-7573-bcd5-ac99636f8bcb