Compare commits

..
Author SHA1 Message Date
Frank 3511e04ae9 docs(jellyfin): record the API-key ledger and the rotation door 2026-11-03 03:27:48 -08:00
Frank 21b7ad15d9 chore(homepage): retire the unreferenced dankserver registry copy
shared/homepage/dankserver.services.json was read by nothing: no reference
anywhere in the repo, and deploy_homepage never copies it. It sat beside the
real registry looking like a second live card set, which is exactly how a
future agent gets misled about what the page renders. Moved to the gitignored
shared/homepage/.AGENTTRASH/ beside it, so it stays recoverable.
2026-11-03 03:23:13 -08:00
Frank 4ad55c28a0 feat(homepage): Jellyfin live, Audiobookshelf to the household page, Marreta says what it is
Jellyfin's card still read "Work in progress: not open to family users yet"
with no link; the server answers 12.4.0 on :8096 and carries a second
(non-admin) account, so the card flips to live status with its Open link on
http://jelly.franksdank.cloud. It has no Authentik link, and the copy says so.

Audiobookshelf moves category admin -> user. A card renders on exactly one
page, so it leaves /admin: Authentik SSO made it a household app, and the
household page is the door that matters.

Marreta's description now names what it is - the content-cleaning proxy in the
Gluetun netns with a headless companion - and its honest limit.

Also in this change:
- docs.html: a "Media & files" line for the audiobook door and its SSO, plus
  the pre-existing unclosed <ul> under frank-dragoon, which made the whole
  frank-archon block parse inside the dragoon bullet.
- Caddyfile: the jelly. vhost gains the exemption reason the External Exposure
  Policy requires (comment only - Caddy strips comments, so no proxy bounce).
- Homepage.md: classification list gains Audiobookshelf, and the section now
  states that services.json is the authority for placement/WIP/link.
2026-11-03 03:20:08 -08:00
dank c9a8469d04 Merge pull request 'docs(audiobookshelf): the wide listener default is a decision, not a defect' (#136) from docs/abs-listener-default-decision into main 2026-11-03 03:09:19 -08:00
Frank 5794b07c32 docs(audiobookshelf): the wide listener default is a decision, not a defect
The SSO paragraph left "whether an access policy should be bound to the
audiobookshelf application" open, which invites the next agent to relitigate
it. Frank's answer of 2026-11-03: accepted on purpose — every signer becomes a
listener with all libraries and downloads, no access policy on the application,
narrowing done per account in Settings -> Users. What that makes load-bearing is
authentik's own signup flow, now named in the same paragraph as the thing that
must stay operator-only.
2026-11-03 03:08:36 -08:00
dank 1a47b831ee Merge pull request 'docs(audiobookshelf): /api/login is not a route — and what auto-registration grants' (#135) from docs/abs-login-path-and-listener-shape into main 2026-11-03 03:04:05 -08:00
7 changed files with 61 additions and 51 deletions

No files matched your search

+8 -1
View File
@@ -290,7 +290,14 @@ audiobooks.franksdank.cloud {
}
}
# --- Jellyfin ---
# --- Jellyfin (exempt by construction: own auth) --------------------------
# No Anubis, no outpost: Jellyfin carries its own complete login (its own
# account store, no Authentik link), which is the policy's exempt category —
# the same family as plex/ABS/*arr around it. Accepted exposure: the sign-in
# form is internet-visible with no rate limiter on either side (this Caddy
# build ships no rate_limit module), so accounts need long unique passwords.
# The homepage card opened it to the household on 2026-11-03; accounts are
# created in Jellyfin's own admin UI, not by SSO.
jelly.franksdank.cloud {
reverse_proxy 192.168.0.235:8096
}
@@ -146,12 +146,15 @@ the local form, and the phone app offers SSO on its server-address screen. The l
With `authOpenIDAutoRegister` on, a first sign-in creates the account and the claim alone
decides its type — and the claim always carries `user`, so **the IdP is now the library gate**:
any authentik account that can reach this application becomes a listener. That is the intent
(the household signs in with the accounts it already has), which makes two things worth
checking when they change: whether authentik's own invitation/signup flow stays something only
Frank drives, and whether an access policy should be bound to the `audiobookshelf` application
to narrow who may even reach the login. Revoking one person is a disable/delete of their ABS
user; revoking everyone is that policy.
any authentik account that can reach this application becomes a listener. That is the
intent (the household signs in with the accounts it already has), and its breadth is
accepted on purpose: no access policy is bound to the `audiobookshelf` application, and
the wide auto-registered default (every library, downloads on) is **not a defect to fix** —
Frank's call, 2026-11-03. Narrowing stays a per-account judgement in **Settings → Users**.
The one thing still worth watching is authentik's own invitation/signup flow: it must
remain something only Frank drives, because an account created there is a listener the
moment it signs in. Revoking one person is a disable/delete of their ABS user; revoking
everyone is that signup flow.
Both halves — authentik's provider row and ABS's settings — are asserted end to end by the Playwright gate named in *Verification*. `23-` signs in as `qa-e2e-admin` and must land inside the app with site-admin rights; `24-` signs in as the standard identity, which is in no Audiobookshelf group, and must land as a listener. Together they pin the escalation and the default: a mapping that stops granting silently locks the household out, and a mapping that over-grants turns the household into site admins.
+25 -1
View File
@@ -3,7 +3,7 @@ type: reference
title: Jellyfin
description: "Jellyfin on Dankserver: quadlet unit, overlay metadata view, provider keys, ports, access, transcode status."
tags: [jellyfin, dankserver, media-server]
timestamp: 2026-11-02T11:00:00Z
timestamp: 2026-11-03T11:40:00Z
---
# Jellyfin
@@ -383,6 +383,30 @@ folder created under `/data/Movies`: it stays invisible until a recursive refres
names it. So every pass that writes into a library ends by firing one, and "Jellyfin
does not see the file I just moved in" is the designed answer, not a fault to chase.
## API-key ledger and rotation
`/Auth/Keys` is the ledger. `GET` lists every key **with its `AccessToken` in
cleartext**, so an admin token in hand prints the whole fleet's keys;
`POST /Auth/Keys?app=<name>` mints one and answers `204` **with no body** — the
new token is read back from the `GET`, never from the create response.
`DELETE /Auth/Keys/{token}` revokes **by the token string**, and a parameter that
matches nothing answers `204` too, so `204` there is not proof anything was
removed. Discover a route's verb from the `Allow` header of an `OPTIONS` (or
`GET`) response — never by firing `DELETE` at a guess.
- `IsActive` reads `False` on rows that authenticate `200`. It is not a usability
signal; only an authenticated request is.
- `DateLastActivity` never moves off `0001-01-01` on 12.4, so "is this key in
use?" cannot be answered from the ledger. The census is repo + host credential
stores + the `Devices` table (whose `DateLastActivity` *is* real).
- Rotation order: mint → rewrite **both** stores (`~/.config/secrets/jellyfin`
and `JELLYFIN_API_KEY` in the ignored `dankserver/.env`) → prove the new key on
`/System/Info` → `DELETE` the old token → prove `401`. Revoking while a store
still holds that value breaks the scan door silently.
- Blast radius as measured 2026-11-03: no repo script reads `JELLYFIN_API_KEY`;
Seerr keeps a key of its own in its own DB (which of its two rows it uses was
never confirmed), so revoke by token and never by app name.
## See Also
- [[Plex]] — co-resident media server on the same volume
@@ -316,7 +316,7 @@ flowchart TD
**User services** (root page), in section order:
- **AI** — OpenWebUI, and the three LLM fleet cards (frank-dragoon, frank-archon, frank-zealot — status + docs link, no Open link).
- **Web Apps** — SearXNG, Gitea, Copyparty, Marreta.
- **Media Library Apps** — Calibre-Web, RomM, Plex, Jellyfin, Seerr.
- **Media Library Apps** — Calibre-Web, RomM, Plex, Jellyfin, Seerr, Audiobookshelf (moved up from the admin page 2026-11-03, once Authentik SSO made it a household app).
**Admin services** (`/admin/`), in section order:
- **Media Library Apps** — the *arr stack (Prowlarr, Sonarr, Radarr, Lidarr, Readarr), qBittorrent, and Tautulli — live on FrankBook-Pro and Dankserver, operator-only: no vhosts (Tautulli included), direct IP:port access only. See [[FrankBook-Pro]].
@@ -324,6 +324,12 @@ flowchart TD
- **AI** — Hermes, ComfyUI.
- **Infrastructure** — Caddy, dnsmasq, Gluetun, and the low-level backends (mariadb, valkey, Postgres). The backends show status only. They get a link only where a UI is meaningful.
**The registry is the authority.** `shared/homepage/services.json` decides which
page a card renders on (`category`), whether it shows a WIP badge instead of live
status (`wip`), and whether it gets an Open link (`showLink` + `url`). The two
lists above are a locator only; when they disagree with the registry, the page is
the bug — fix the page.
## Layout
```
-35
View File
@@ -1,35 +0,0 @@
{
"host": "dankserver",
"services": [
{
"name": "Homepage",
"url": "/",
"description": "This page. Password-protected service index.",
"docs": "serverAppConfigs-docs/Infrastructure/Homepage.md"
},
{
"name": "RomM",
"url": "http://romm.franksdank.cloud",
"description": "Retro game manager and player for the ROM collection.",
"docs": "serverAppConfigs-docs/Dankserver/RomM.md"
},
{
"name": "Copyparty",
"url": "http://files.franksdank.cloud",
"description": "High-speed file sharing and streaming server.",
"docs": "serverAppConfigs-docs/Dankserver/Copyparty.md"
},
{
"name": "Calibre-Web",
"url": "http://books.franksdank.cloud",
"description": "E-book library browser and reader.",
"docs": "serverAppConfigs-docs/Dankserver/Calibre-Web.md"
},
{
"name": "Plex",
"url": "http://plex.franksdank.cloud/web/index.html",
"description": "Media server (existing host service, proxied).",
"docs": "serverAppConfigs-docs/Dankserver/Dankserver Stack.md"
}
]
}
+5
View File
@@ -56,6 +56,10 @@
<a href="https://jelly.franksdank.cloud">Jellyfin</a>. Seerr is the front door —
requests ride the VPN tunnel from there.</li>
<li><strong>Books:</strong> <a href="https://books.franksdank.cloud">Calibre-Web</a> — the e-book library, with its own login.</li>
<li><strong>Audiobooks:</strong> <a href="https://audiobooks.franksdank.cloud">Audiobookshelf</a> &mdash; the
audiobook library. Same single sign-on as everything else: choose
<strong>Continue with authentik</strong> and each listener keeps their own
position.</li>
<li><strong>Games:</strong> <a href="https://romm.franksdank.cloud">RomM</a> — ROM library and emulator collection.</li>
<li><strong>Raw files:</strong> <a href="https://files.franksdank.cloud">Copyparty</a> — direct downloads and shares.</li>
<li><strong>Search:</strong> <a href="https://search.franksdank.cloud">SearXNG</a> — metasearch, VPN-routed, no AI snoop. <details class="key-warn"><summary>&#9888; privacy note</summary><div class="warn-body">Searches are obfuscated, not anonymous: SearXNG hides you from the upstream engines and the VPN tunnel hides the traffic from the LAN &mdash; but the upstream search API keys are tied to Frank&rsquo;s identity. Don&rsquo;t treat it as Tor.</div></details></li>
@@ -109,6 +113,7 @@
<li><strong>Best for:</strong> complex agentic tasks, programming, system administration, and "claw"-style personal agents.</li>
<li><strong>Supervision:</strong> safe for autonomous, low-supervision use.</li>
<li><strong>Warning:</strong> <details class="key-warn"><summary>&#9888; uncensored model</summary><div class="warn-body">Uncensored model: output can include adult content, content that describes harmful, hateful, or illegal acts, content concerning &ldquo;sensitive offensive cybersecurity topics&rdquo;, and other output that may carry severe legal risk. Take this warning seriously &mdash; don&rsquo;t ruin this server for everybody.</div></details></li>
</ul>
<h2>frank-archon — the big one</h2>
<ul>
+7 -7
View File
@@ -110,13 +110,13 @@
"name": "Jellyfin",
"category": "user",
"section": "Media Library Apps",
"description": "Open source media server. Work in progress: not open to family users yet.",
"url": "",
"description": "Open source media server for the film and TV library: its own logins (no Authentik here), so ask Frank for an account.",
"url": "http://jelly.franksdank.cloud",
"docs": "",
"probe": "192.168.0.235:8096",
"method": "http",
"wip": "down",
"showLink": false,
"wip": false,
"showLink": true,
"art": "/img/apps/jellyfin.svg"
},
{
@@ -136,7 +136,7 @@
"name": "Marreta",
"category": "user",
"section": "Web Apps",
"description": "Clean, ad-free article reader. Paste any URL, get just the article.",
"description": "Paywall sledgehammer: hand it an article URL and it drags the page back through the VPN with the paywall chrome, cookie wall and trackers stripped; a headless browser renders the JS-heavy ones. Best effort — hard paywalls and login walls still win.",
"url": "http://marreta.franksdank.cloud",
"docs": "",
"probe": "127.0.0.1:8082",
@@ -513,9 +513,9 @@
},
{
"name": "Audiobookshelf",
"category": "admin",
"category": "user",
"section": "Media Library Apps",
"description": "Audiobook server for the entropy library: its own SQLite library, its own login, per-listener positions.",
"description": "Audiobook server for the library: sign in with your Authentik account and every listener keeps their own position.",
"url": "http://audiobooks.franksdank.cloud/audiobookshelf/",
"docs": "",
"probe": "192.168.0.235:8000",