Files
computer-playbook/docs/requirements/030-web-app-jellyfin.md
kevinveenbirkenbach 9ee4cd0fb9 Merge branch 'main' of github.com:infinito-nexus/core into feature/swarm-nfs
# Conflicts:
#	docs/agents/action/iteration/compose.md
#	roles/svc-db-openldap/vars/main.yml
#	roles/svc-runner/defaults/main.yml
#	roles/svc-runner/tasks/02_cleanup_instance.yml
#	roles/web-app-discourse/meta/server.yml
#	roles/web-app-mailu/tasks/01_core.yml
#	roles/web-app-odoo/tasks/main.yml
2026-07-04 18:16:12 +02:00

7.2 KiB

030 - Jellyfin Role with LDAP / OIDC SSO

User Story

As a platform administrator of Infinito.Nexus, I want Jellyfin integrated as a web-app-jellyfin role with Keycloak-backed Single Sign-On so that users can stream the media server behind the platform's central identity, validated end-to-end with Playwright personas.

Background

Jellyfin is a free, open-source media server. The official jellyfin/jellyfin container is Debian-based, stores everything in an internal SQLite DB (no external database), and persists three volumes: /config, /cache, /media.

Upstream facts established by research

Topic Finding Source
Image jellyfin/jellyfin (Docker Hub + ghcr.io); tags latest, X.Y, X.Y.Z. Pin a concrete 10.x patch. Container install
Config JELLYFIN_* env (double-underscore nesting), JELLYFIN_PublishedServerUrl, TZ; runs as PUID/PGID. Configuration
Ports 8096 (HTTP web), 8920 (optional HTTPS), 7359/udp + 1900/udp (client/service discovery). Container install
Database None external — internal SQLite under /config. Configuration
Native auth Local Jellyfin users only — no native OIDC/LDAP. Discussion #16470
LDAP plugin jellyfin-plugin-ldapauth — official-catalog plugin; works on ALL clients (web + native apps). LDAP self-hosting
OIDC/SSO plugin 9p4/jellyfin-plugin-sso — OIDC/SAML one-click, web UI only (native apps cannot use it); third-party. plugin-sso
oauth2-proxy gate Breaks native apps (they authenticate with API tokens, cannot run the browser OIDC flow). Not suitable as the sole gate for a media server. Discussion #16470

In-repo analogues

  • Because Jellyfin has no native SSO and its apps use API tokens, the LDAP plugin against the central svc-db-openldap is the "works everywhere" path — closest in spirit to roles that consume central LDAP. The optional OIDC web-SSO plugin maps to the platform Keycloak.
  • No external DB consumer (like Checkmk).

Confirmed Decisions

These decisions were confirmed by the operator before implementation starts and are NOT subject to re-litigation during implementation.

# Decision Rationale
1 Canonical hostname media.{{ DOMAIN_PRIMARY }}. Standard single-host convention.
2 Both auth plugins: jellyfin-plugin-ldapauth (central OpenLDAP, all clients) and 9p4/jellyfin-plugin-sso (Keycloak OIDC, web one-click). Operator decision: LDAP gives every client (incl. native apps) a platform identity; the OIDC plugin adds one-click web sign-in.
3 Media library on a named Docker volume (/media), plus /config + /cache. Operator decision: self-contained v1; libraries added in-app.
4 CPU transcoding only (no GPU passthrough). Operator decision: smallest, host-portable surface.
5 Image jellyfin/jellyfin pinned to a concrete stable 10.x patch (no :latest). Standard upstream-pin policy.
6 No external DB; internal SQLite under /config. Jellyfin bundles SQLite.

Target Schema

Role layout

roles/web-app-jellyfin/
├── README.md
├── files/playwright/*.js
├── meta/{main,info,server,services,schema,users,rbac,variants,volumes}.yml
├── tasks/{main,01_core}.yml
├── templates/{compose.yml.j2,env.j2,playwright.env.j2}
└── vars/main.yml

Acceptance Criteria

Routing & TLS

  • media.{{ DOMAIN_PRIMARY }} resolves through sys-svc-proxy to Jellyfin (8096); GET / returns the Jellyfin web UI; JELLYFIN_PublishedServerUrl is set to the canonical URL.
  • The role emits a Content-Security-Policy header on the canonical surface.

Role layout & image

  • roles/web-app-jellyfin/ exists per the Target Schema; image pinned to a concrete 10.x patch (no :latest).
  • All meta/*.yml exist and pass the role-meta lint.

Bootstrap

  • First run reaches a steady running state with /config + /cache (+ media per Q2) persisted; the initial-setup wizard is bypassed or a first admin is seeded where supported.

Authentication (Decision #2)

  • The chosen auth integration is provisioned non-interactively: for LDAP, jellyfin-plugin-ldapauth is installed and configured against svc-db-openldap (server/bind/search/attributes); for the OIDC plugin, it is installed and pointed at Keycloak.
  • An LDAP/SSO user can sign in and reach the Jellyfin home; a dedicated Playwright scenario (gated on the chosen service) covers it.

Variants

  • meta/variants.yml defines three variants (V1 all auth on, V2 all-false, V3 ldap-only); all deploy cleanly on a fresh box.

Playwright

  • Guest + authenticated persona(s) per the playwright contract; templates/playwright.env.j2 exposes every read env var; make compose-playwright role=web-app-jellyfin exits 0 with no stub tests and a clean logged-out final state.

Health & quality

  • Stack steady across all three variants; make test green tree-wide.

Documentation

  • README.md documents image + bump policy, the auth approach + its client-coverage caveat (web-only vs all-clients), the media-storage decision, transcoding, and the no-external-DB design.
  • This requirement is cross-linked from the implementing PR.

Validation Apps

INFINITO_APPS="web-app-jellyfin" make deploy-fresh-purged-apps INFINITO_FULL_CYCLE=true

Smoke: visit https://media.{{ DOMAIN_PRIMARY }}/ → Jellyfin loads → sign in via the chosen integration → reach the home/library surface.

Prerequisites

Before implementation, the agent MUST read AGENTS.md, then Role Loop and the Playwright contract.

Implementation Strategy

Execute autonomously after the Open Questions are resolved. Verify the plugin install + config mechanism (plugin DLL placement under /config/plugins, the plugin configuration XML schema, and the catalog/version) against the pinned upstream plugin release before claiming done.

Commit Policy

  • No git commit until every Acceptance Criterion is checked off.
  • When met and make test is green, instruct the operator to run git-sign-push outside the sandbox. The agent MUST NOT push.

Context