Commit Graph
18 Commits
Author SHA1 Message Date
Frank a14a70a404 dankserver: copyparty PublishPort=3923 + seam pinning host-served ports
The cutover unit joined dankserver_appnet but dropped the compose-era
0.0.0.0:3923 mapping (live inspect: bridge, 3923 published), so
copyparty listened inside the netns while the host refused. New seam
asserts every host-facing port matches the pre-cutover PortBindings:
romm 8085->8080, jellyfin 8096, calibre-web 8083, copyparty 3923.
2026-09-01 13:53:10 -07:00
Frank 06de2b4cbf dankserver: jellyfin GPU device via AddDevice=, seam guard for Device=
The podman 5.x v1 quadlet generator rejected the whole jellyfin unit on
the unsupported Device= key (generator log: unsupported key 'Device' in
group 'Container'), so no jellyfin.service was generated and the cutover
restart failed with 'Unit not found' after the data root had already
migrated cleanly. gluetun proves AddDevice= is the house key. New seam
test denies Device= across all six dankserver units and pins the
AddDevice=/dev/dri replacement.
2026-09-01 13:49:45 -07:00
Frank b206f477ad dankserver: quadlet units + deploy handlers for romm trio, jellyfin, calibre-web, copyparty
Cutover of the six surviving dankserver services from compose-era bare
containers to boot-persistent Quadlet units (WantedBy=default.target; podman
5.x v1 generator honors it at user-manager start).

Units are faithful to live inspect ground truth (image pins = running tags,
not compose inventory):
- romm trio shares one data root: /home/dank/Containers/romm mounted whole at
  /romm; mariadb datadir and redis data dir live inside it. Aliases mariadb/
  redis preserved on dankserver_appnet via Network=...:alias=. Secrets stay in
  the host-local env file (EnvironmentFile + ${VAR} expansion in the generated
  ExecStart, gitea precedent). copyparty passes its admin account via
  Exec=-a ${COPAY_ACCOUNT}:${COPAY_PASSWORD} (Exec= not Command=: the v1
  generator hard-fails on Command=); entropy stays rw only there.
- jellyfin keeps every creation-time env as Environment= lines (zero drift);
  Device=/dev/dri + GroupAdd=keep-groups preserve hardware transcoding.
- calibre-web pins the exact running image tag (id 986277563e76 =
  0.6.27-ls397), pasta network preserved.

Deployer: first-cutover handlers retire the old container, rescue unshadowed
anonymous-volume content (podman volume export + tar --skip-old-files), mv
data roots on the same fs, then start units in dependency order with health
gates. Re-deploys restart only the selected unit. deploy-plan.py maps compose
keys mariadb/redis to the new unit names; allowlists updated. New seam test
tests/dankserver_quadlet.bats pins boot-persistence + v1-generator contract;
test-changed.sh maps the new dirs to it. Full suite green (209 tests).
2026-09-01 04:14:08 -07:00
Frank 8e1b00646f copyparty: read-only volume (no uploads); correct runbook anon-perms claim
-v /w::A granted anonymous full rwmda on the LAN (verified: anon PUT -> 201,
PROPFIND -> 207). New perms ::r:ra,dank: browse/download for anonymous
(required by the guarded files.franksdank.cloud chain), admin for dank,
no w/m/d for anyone. Runbook corrected with the --help-accounts semantics.
2026-08-31 16:37:38 -07:00
Frank b9de886509 fix(dankserver): run arr stack and qb as container-root (PUID=0)
Under rootless podman, LSIO's PUID=1000 maps to host uid 101000 — a phantom
user owning nothing: Sonarr's own validator rejected /tv because the process
could not write it. Container-root under a single-user userns maps to the host
user (dank), which owns every /mnt/entropy media dir and /opt config dir.
Identity is now mapping-independent instead of depending on subuid offsets.
calibre-web untouched (working; churn avoided).
2026-08-29 07:18:50 -07:00
Frank 2c8e65e82f fix(dankserver): copyparty ac edition takes accounts and volumes as argv flags
Ground truth from the image itself: python3 -m copyparty --help-accounts shows
-a takes USER:PASS and -v takes SRC:DST:PERMS:FLAGS — the COPAY_PASS/COPAY_MODE
envs this compose carried are not consumed by this image at all (zero COPAY_
references in the package). With no accounts and no volumes configured, the ac
edition serves its cwd read/write for everyone: the stack was running an
unauthenticated world-writable share of the 91T array. Now: admin account dank
with the A flag on volume /w; anonymous gets nothing.
2026-08-29 03:32:18 -07:00
Frank f3ebe0943a revert(dankserver): drop copyparty command override
The ac edition's entrypoint (cpp.sh -> python3 -m copyparty) consumes config
via /z/initcfg, not argv; -v is the volume flag and ate the next arg,
crash-looping argparse. The image mounts everything under /cfg/copyparty as
volumes by marker-file convention — plant the marker on the host instead.
2026-08-29 03:26:46 -07:00
Frank 9cbd8b8822 fix(dankserver): copyparty ac edition takes volumes as command args
The image entrypoint is /bin/ash /z/cpp.sh -c /z/initcfg: copyparty flags are
passed through command, and the ac edition mounts volumes with -m, not via a
COPAY_VOL env. Without any volume mounted every request resolved zero volumes
and the app answered HTTP 500 with its own error page. Verbose flag included
for the bring-up; drop it once stable.
2026-08-29 03:19:41 -07:00
Frank c70d203ad9 fix(dankserver): copyparty volume root via COPAY_VOL
The ac edition maps volumes from COPAY_* envs; without a volume root every
request resolves zero volumes and the app answers HTTP 500 with its own error
page. Point it at /w where the LUKS array is mounted.
2026-08-29 03:17:02 -07:00
Frank 03fd9e244d fix(dankserver): copyparty serves /w from the array; image is the ac edition
ghcr.io/9001/copyparty:latest does not exist — ghcr answers 403 on the token
request for nonexistent repos, which is why the bring-up failed at pull. The
upstream publishes ghcr.io/9001/copyparty-ac (ac = full edition) and serves
whatever is mounted at /w, so the compose mounts the LUKS array there; config
persists in /opt/copyparty/cfg per upstream layout.
2026-08-29 03:09:27 -07:00
Frank d74ea5bf39 fix(dankserver): jellyfin user-mapping and readarr image tag
Jellyfin ran as user 1000:1000 inside a rootless user namespace, where host
uid 1000 maps to container uid 0 — the pinned uid is unmapped, so /config/log
creation EACCES'd and the service SIGSEGV'd on boot. Container root (= host
dank) is correct under rootless podman; group_add 992 (render) still maps via
the userns group map, so GPU access is preserved.

Readarr: lscr.io/linuxserver/readarr:latest does not exist — LSIO ships only
arch-prefixed nightly/version tags for readarr (verified against the ghcr tag
API). Pin amd64-1.0.980, the newest stable tag.
2026-08-29 03:03:55 -07:00
Frank 67de5cfe9b fix(dankserver): point media mounts at the real entropy array paths
The compose stack mounted /mnt/media/{movies,shows,tv,music,books,downloads}
— a prefix that has never existed on this host. The 91T LUKS array mounts at
/mnt/entropy and its libraries are case-exact (Movies, TVShows, Music,
eBooks). Also restores prowlarr's config mount (eaten by an off-by-one in the
prior edit) and fixes TZ New_York->Los_Angeles on the arr stack.
2026-08-29 02:54:24 -07:00
Frank 4a679fce80 Caddy consolidation: single Caddy on dankclaw fronts both hosts
Per user direction, Caddy runs only on dankclaw. It fronts every
service on both hosts: local services by container name, quadlet
services by published LAN port, dankserver services by published LAN
port (192.168.0.235:PORT).

- dankclaw/Caddyfile: merged vhost list (was: two Caddyfiles, one per
  host). gitea's single vhost moves in from the retired gitea-caddy
  quadlet (option B). Upstreams fixed to live reality: gitea :3000,
  search :8081 (gluetun), chat :3005 (open-webui quadlet), media
  services via 192.168.0.235. Retired: llms (open-webui on dankserver
  is removed; chat is its successor), the dankserver homepage (302 to
  the shared one).
- dankserver/Caddyfile, dankclaw/gitea/Caddyfile: deleted (git rm).
- dnsmasq addn-hosts: every name -> 192.168.0.143 (the one Caddy
  host); no .235 records, no llms.
- compose.yaml: removed the openwebui/postgres/gitea services (they
  are quadlets now); caddy service mounts the rendered Caddyfile from
  /home/dank/Containers/caddy, healthcheck probes the bare-IP
  catch-all (302).
- deploy.sh: per-target logic (dankserver = pull-only, health via the
  shared Caddy at 192.168.0.143); renders CADDY_BASICAUTH_HASH from
  the host .env into the mounted Caddyfile.
- .env.example: CADDY_BASICAUTH_HASH on dankclaw; Porkbun block
  dropped from dankserver (its Caddy is gone); SEARXNG_BASE_URL to
  the flat name; stale OPENAI_* block dropped (quadlet is
  self-contained).
- tests: caddyfile.bats now asserts the single-Caddyfile invariant;
  dnsmasq.bats cross-checks vhosts -> .143 and the retired names.
2026-08-21 21:44:18 -07:00
Frank 1dee2b737e dnsmasq: serve config from /home/dank/Containers/dnsmasq (rootless-correct)
/opt is root-owned; the established data-location pattern (see
docs/Architecture/Storage Policy.md, open-webui quadlet) is
/home/dank/Containers/<service>.
2026-08-21 16:41:49 -07:00
Frank 55d062ea7b DNS names: flat franksdank.cloud scheme + dnsmasq LAN DNS on dankclaw
Phase 1 of the local-DNS-name work. Users type a name, not a port.

- dnsmasq quadlet on dankclaw (rootless, slirp4netns, port 53 published):
  serves the flat name scheme from addn-hosts, upstream 1.1.1.1/1.0.0.1.
  Image built from the in-repo Containerfile (localhost/dnsmasq:latest).
- Caddyfiles (both hosts): all *.dankclaw.local / *.dankserver.local vhosts
  renamed to flat <service>.franksdank.cloud. Explicit http:// site
  addresses for now; TLS (wildcard LE via DNS-01/Porkbun) lands in phase 2.
- Drift reconciliation (live /etc/caddy/Caddyfile on dankserver differs
  from the repo): added the live-only llms.franksdank.cloud vhost
  (OpenWebUI on :3000). Dropped the legacy search vhost from dankserver
  (the name belongs to dankclaw's gluetun-routed SearXNG; the legacy
  instance stays reachable at 192.168.0.235:8081).
- Homepage service links updated to the new names.
- New test seams (TDD): tests/dnsmasq.bats (quadlet boot-persistence,
  port 53, conf/record validity, Caddyfile<->addn-hosts cross-check on
  the right host IPs); caddyfile.bats naming-convention tests updated.

LAN clients get this DNS from the SURFboard G36 (DNS Relay -> statically
configured -> 192.168.0.143); the router change is a pending user step.
2026-08-21 16:31:42 -07:00
Frank 833f2365f1 env: add Porkbun API token placeholders (DNS-01 cert issuance)
Real tokens live only in the host .env files (git-ignored):
- /home/dank/serverAppConfigs/dankclaw/.env
- /home/dank/serverAppConfigs/dankserver/.env

Token is scoped to the franksdank.cloud zone (DNS read/write).
2026-08-21 16:00:16 -07:00
Frank e8fd009419 docs: fact-check sweep, Jellyfin, *arr stack, and ROM scraping guides
Fact-check results:
- RomM 5.2.0: MariaDB+Redis now required (was SQLite), auth var renamed, 11 metadata providers
- Copyparty v1.20.19: 3 CVEs fixed, WOPI integration, docker editions documented
- Calibre-Web 0.6.27-ls397: version pinning, kepubify auto-config, CVE fixes
- Calibre 9.9.0: content server security fix noted

New documentation:
- docs/Dankserver/Jellyfin.md — full media server guide (compose, Caddy, GPU, metadata, clients)
- docs/Dankserver/ARR-Stack.md — automated download stack overview
- docs/Dankserver/RomM-Scraping.md — metadata providers, naming, cover art, emulator integration
- docs/Dankserver/sonarr.md, radarr.md, lidarr.md, readarr.md, prowlarr.md, qbittorrent.md
- docs/Dankserver/mariadb.md, redis.md — supporting services for RomM

Infrastructure updates:
- dankserver/compose.yaml: added mariadb, redis, jellyfin, *arr services
- dankserver/Caddyfile: added hostnames for all new services
- dankserver/.env.example: added all new environment variables
- docs/index.md: linked all new documentation pages

Tested: 52/52 tests pass.
2026-08-20 23:40:07 -07:00
Frank e3eb5334cb feat: initialize serverAppConfigs with podman stacks for Dankserver and Dankclaw
- Dankserver stack: RomM, Copyparty, Calibre-Web, node-exporter
- Dankclaw stack: Gluetun VPN (SearXNG tunnel), SearXNG+Valkey, ComfyUI
  (CPU backend), OpenWebUI, Hermes, Gitea+Postgres (migrated), Caddy
  reverse proxy, Grafana+Prometheus+Loki+Alloy observability
- Caddyfiles with .local hostname convention and basicauth homepage
- bind-mount storage policy, no named volumes
- deploy.sh mirroring vllmScripts git workflow (push -> pull -> restart -> health)
- docs/ Obsidian vault: per-service docs from frank-default-small research
  subagents (promtail EOL -> Alloy; gitea SECRET_KEY preservation; ComfyUI --cpu)
- bats test suite at 6 seams — 22 tests green
2026-08-13 20:19:41 -07:00