halkyon/deploy/sidecars/README.md
Tizian 「ティツィアーン」 073475001c
All checks were successful
CI / check (push) Successful in 15m54s
CI / deploy (push) Successful in 15s
docs(deploy): the Jellyfin demo's account never locks
With Jellyfin's default of three failed tries, anyone could switch the
demo off with three wrong passwords; an account without a password has
nothing for the lockout to guard. The runbook also says how people sign
in to it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-14 01:55:12 +02:00

98 lines
4.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Sidecars
Services an instance may run beside itself (decided 2026-10-14), each switched on by
naming its compose file after the instance's own in `deploy/.env`:
```sh
COMPOSE_FILE=compose.yaml:compose.jellyfin.yaml
```
A sidecar's compose file adds its service and, if it is reached from outside, mounts
its site from this folder into Caddy's `/etc/caddy/sidecars/`, which the Caddyfile
imports. It joins the `edge` network beside Caddy and the server's public API, never the
internal one where Postgres is, and publishes no port of its own. Its process runs as a
user ID of its own, which no person, no other container and not the CI runner uses.
`redeploy.sh` deploys whatever `.env` switches on, and recreates Caddy when a sidecar's
site changes.
| Sidecar | Compose file | Site |
|---|---|---|
| Jellyfin, which people watch and listen to learn from | `compose.jellyfin.yaml` | `jellyfin.caddy` |
## Jellyfin
A Jellyfin server beside the instance, which the instance then suggests to its people
(`JELLYFIN_URL`, set by the compose file): the app's Jellyfin sign-in fills it in and
lists the people it shows on its login screen. Videos stream as stored, nothing is
transcoded. Caddy refuses every change to a user from outside, so an account without a
password (a demo's) stays everyone's; the admin signs in through an SSH tunnel only.
Steps, in order; ✱ marks a step as root.
1. ✱ **Its user and folders** (the operator, on the instance's server, as a user with
sudo): the user and group ID 61096 are Jellyfin's alone, as the compose file runs it
(`useradd` warns that the ID lies above the system range, which is meant: it must
not meet a container's IDs); the media folder is `deploy`'s, so it can copy files
in, readable by all.
```sh
sudo groupadd --system --gid 61096 jellyfin
sudo useradd --system --uid 61096 --gid 61096 --no-create-home --shell /usr/sbin/nologin jellyfin
cd ~deploy/halkyon-learn/deploy
sudo mkdir -p jellyfin/config jellyfin/cache jellyfin/media
sudo chown -R jellyfin:jellyfin jellyfin/config jellyfin/cache
sudo chown -R deploy:deploy jellyfin/media
```
2. **DNS** (the operator, at the domain's DNS): the Jellyfin domain, such as
`jellyfin.example.org`, pointing at the instance's server (`A`, and `AAAA` if it has
an IPv6 address).
3. **Switch it on** (the operator, on the instance's server, as `deploy`): in
`deploy/.env`,
```sh
COMPOSE_FILE=compose.yaml:compose.jellyfin.yaml
JELLYFIN_DOMAIN=jellyfin.example.org
```
4. **The media** (the operator, on their own machine): a folder per film, show or
album, as Jellyfin expects them, into `deploy/jellyfin/media/`, readable by all;
through `tar` and SSH, which every server has (rsync would have to be installed on
it too):
```sh
tar -C "<the folder's parent>" -cf - "<folder>" |
ssh <deploy's ssh alias> 'tar -C halkyon-learn/deploy/jellyfin/media/movies -xf -'
```
5. **Start it** (the operator, on the instance's server, as `deploy`):
`cd ~/halkyon-learn/deploy && docker compose up -d`. Caddy and the server are
recreated with it (the site, and the instance's suggestion); the instance is away
for seconds.
6. **Set Jellyfin up through a tunnel** (the operator, on their own machine):
`ssh -N -L 8096:172.30.80.10:8096 <deploy's ssh alias>`, then http://localhost:8096.
- The wizard: an admin of a name nobody guesses, its password in a password
manager; a library of the type Movies (or Shows, Music) on `/media/…`; remote
connections allowed.
- Dashboard › Networking › **Known proxies**: `172.30.80.2` (Caddy's address on
`edge`). Jellyfin then sees each visitor's own address, and the tunnel as local.
- Dashboard › General: **Quick Connect** off, unless people sign in with it.
- Dashboard › Users › the admin: **Hide this user from login screens**; **Allow
remote connections to this server** off.
- For a demo, Dashboard › Users › **+**: an account without a password, shown on
login screens, seeing the demo's library alone; off: playback and conversion that
need transcoding, content downloading, remote control of other users, deleting
media. On its Profile tab, **Failed login tries before user is locked out** `-1`:
with Jellyfin's default of 3, anyone could switch the demo off with three wrong
passwords, and without a password the lockout guards nothing. People sign in to it
by tapping it and leaving the password empty: Jellyfin doesn't say which accounts
have none.
7. **Check from outside** (anyone, anywhere): `https://<domain>/System/Info/Public`
answers, `/Users/Public` lists the demo account, a `POST` to `/Users/Password` gets
403, and the instance's `instance.info()` names the domain as `jellyfinUrl`.
Switching it off: the compose file out of `COMPOSE_FILE`, then `docker compose up -d
--remove-orphans`; `deploy/jellyfin/` stays until removed by hand.