diff --git a/.env.example b/.env.example
index 06681a3..52eb9b6 100644
--- a/.env.example
+++ b/.env.example
@@ -76,6 +76,9 @@ FLARESOLVERR_CAPTCHA_SOLVER=none
# SABnzbd (Usenet) — credentials in secrets/.env, not here
SABNZBD_PORT=8085
+# Caddy landing page (LAN service map)
+LANDING_PORT=80
+
# Forgejo — self-hosted Git (web + SSH clone)
FORGEJO_HTTP_PORT=3002
FORGEJO_SSH_PORT=2222
diff --git a/README.md b/README.md
index 31c0984..b131690 100644
--- a/README.md
+++ b/README.md
@@ -9,6 +9,7 @@ for the **Beelink SER5 PRO** (Ryzen 7 7735HS + Radeon 680M).
| Service | URL | Purpose |
|-------------|------------------------|--------------------------------------|
+| **Landing** | http://host/ | Dark map of all LAN services (Caddy) |
| Jellyfin | http://host:8096 | Media server / playback |
| qBittorrent | http://host:8080 | Torrent client (VPN-only) |
| Prowlarr | http://host:9696 | Indexer manager |
diff --git a/ansible/group_vars/all.yml b/ansible/group_vars/all.yml
index 1d0d839..1bbd05e 100644
--- a/ansible/group_vars/all.yml
+++ b/ansible/group_vars/all.yml
@@ -173,6 +173,7 @@ ufw_allow_ssh_from_anywhere: false
ufw_lan_tcp_ports:
- { port: 22, comment: "SSH" }
+ - { port: 80, comment: "Landing (Caddy)" }
- { port: 8080, comment: "qBittorrent" }
- { port: 8096, comment: "Jellyfin" }
- { port: 8989, comment: "Sonarr" }
diff --git a/docker-compose.yml b/docker-compose.yml
index 451e496..4ab65d6 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -8,6 +8,7 @@ name: arr-stack
# sabnzbd Usenet NZB download client (Newshosting, Tweaknews, …)
# sonarr TV radarr movies bazarr subtitles
# jellyfin media server
+# landing Caddy — dark LAN homepage (port 80)
#
# AI / coding stack (local, no VPN)
# bonsai Prism Ternary-Bonsai-27B via llama-server (OpenAI-compatible API)
@@ -186,6 +187,19 @@ services:
# devices:
# - /dev/dri:/dev/dri
+ # ----------------------------------------------------- LAN landing (Caddy) #
+ # Dark-mode map of services for other PCs on the network: http://server/
+ landing:
+ image: caddy:2-alpine
+ container_name: landing
+ restart: unless-stopped
+ networks: [arr-net]
+ ports:
+ - "${LANDING_PORT:-80}:80"
+ volumes:
+ - ./landing/Caddyfile:/etc/caddy/Caddyfile:ro
+ - ./landing/site:/srv:ro
+
# ------------------------------------------------------------------- music #
lidarr:
image: lscr.io/linuxserver/lidarr:latest
diff --git a/landing/Caddyfile b/landing/Caddyfile
new file mode 100644
index 0000000..e52e810
--- /dev/null
+++ b/landing/Caddyfile
@@ -0,0 +1,7 @@
+# Homelab landing page — LAN only (UFW allows 80 from LAN)
+:80 {
+ root * /srv
+ encode gzip
+ file_server
+ try_files {path} /index.html
+}
diff --git a/landing/site/index.html b/landing/site/index.html
new file mode 100644
index 0000000..4a4105f
--- /dev/null
+++ b/landing/site/index.html
@@ -0,0 +1,428 @@
+
+
+
+
+
+ Homelab · Resource map
+
+
+
+
+
+
+
Homelab resource map
+
+ Everything below is available to PCs on this home network.
+ Click a card to open it. New here? Read the short guide at the bottom.
+
+
server…
+
+
+
+
+
+
+
+
Quick start for humans
+
+
Watch movies & shows — open Jellyfin. Create a user if asked. Libraries are already on the server.
+
Request / automate media — use Sonarr (TV), Radarr (movies), Lidarr (music). They search via Prowlarr and download with torrents or Usenet.
+
Downloads — qBittorrent is for torrents (goes through the VPN). SABnzbd is for Usenet/NZB files. You usually don’t open these unless something is stuck.
+
Talk to the local AI — Open WebUI is the chat box. The model runs on this mini PC (private, no cloud).
+
Code & git — Forgejo is our GitHub-like site. OpenHands is an AI coding agent (optional; start it if the link fails).
+
Only on this Wi‑Fi / LAN — these links use the server’s local address. They won’t work from the mobile internet unless you set up remote access later.
+
+
+
+
+
+
+
+
+
diff --git a/quickstart.md b/quickstart.md
index 8b720ee..1afc74a 100644
--- a/quickstart.md
+++ b/quickstart.md
@@ -61,6 +61,7 @@ LAN URLs (UFW allows **`192.168.8.0/24`**). Replace host if needed.
| Service | URL |
|---------|-----|
+| **Landing page** | http://192.168.8.123/ |
| Jellyfin | http://192.168.8.123:8096 |
| qBittorrent | http://192.168.8.123:8080 |
| Prowlarr | http://192.168.8.123:9696 |
@@ -111,13 +112,30 @@ Root folders: `/data/media/tv`, `/data/media/movies`, `/data/media/music` (hardl
**Prowlarr** — Add indexers. Apps: Sonarr `http://sonarr:8989`, Radarr `http://radarr:7878`, Lidarr `http://lidarr:8686` + each API key (*Settings → General*).
+**Usenet (NZBgeek + Newshosting + Tweaknews)**
+Credentials live in **`secrets/.env`** (not git). Example keys: see `secrets/.env.example`.
+
+```bash
+# on ser5
+cd /opt/stack && git pull
+mkdir -p /storage/usenet/{incomplete,complete/{movies,tv,music}}
+# copy secrets if needed: scp secrets/.env tim@stack:/opt/stack/secrets/
+docker compose up -d sabnzbd
+# open http://192.168.8.123:8085 once if first-run wizard appears, then:
+./scripts/configure-usenet.sh
+```
+
+1. **SABnzbd** (http://host:8085) — Config → Servers: Newshosting `news.newshosting.com:563` SSL; Tweaknews `news.tweaknews.eu:563` SSL. Test both green.
+ Folders: incomplete `/data/usenet/incomplete`, complete `/data/usenet/complete` (categories `movies`/`tv`/`music`).
+2. **Prowlarr** → Indexers → **NZBgeek** → API key from secrets (`NZBKEEPKEY` / `NZBGEEK_API_KEY`).
+ Download Clients → **SABnzbd** → host `sabnzbd`, port `8080`, API key from SABnzbd Config → General.
+ Sync download client to apps (or add SABnzbd in each *arr).
+3. **Sonarr/Radarr/Lidarr** → Download Clients → SABnzbd: host `sabnzbd`, port `8080`, categories `tv` / `movies` / `music`.
+
+Usenet traffic is **not** forced through gluetun (SSL to provider). Torrents still go via VPN.
+
**FlareSolverr** (Cloudflare / JS challenges for some indexers) — container `flaresolverr` on `arr-net`, no public port by default.
-Prowlarr → **Settings → Indexers** (or **Settings → Connect** / FlareSolverr section depending on version) → add FlareSolverr host:
-
-- Host: `http://flaresolverr:8191`
-- Then enable FlareSolverr on indexers that show Cloudflare errors.
-
-Does **not** solve every captcha (Turnstile/hCaptcha image puzzles, etc.). Prefer indexers without CF; paid APIs (2captcha / CapSolver) are optional external services, not a free self-hosted magic box.
+Prowlarr → **Settings → Indexers** → FlareSolverr host: `http://flaresolverr:8191`.
**Bazarr** — Sonarr/Radarr URLs + API keys; paths `/data/media/tv`, `/data/media/movies`.