Files
homelab-optimized/docs/superpowers/plans/2026-04-24-pinchflat.md
Gitea Mirror Bot 48bfce60e7
Some checks failed
Documentation / Build Docusaurus (push) Failing after 5m7s
Documentation / Deploy to GitHub Pages (push) Has been skipped
Sanitized mirror from private repository - 2026-04-24 08:27:14 UTC
2026-04-24 08:27:14 +00:00

15 KiB
Raw Blame History

Pinchflat Test Deployment Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: Deploy Pinchflat (YouTube auto-archiver) on Atlantis via a hand-run docker compose on a feature branch, so the user can evaluate it before committing to production promotion.

Architecture: Single ghcr.io/kieraneglin/pinchflat:latest container, port-published on 8945 on Atlantis's LAN IP. Config on Atlantis NVMe (/volume2/metadata/docker2/pinchflat/config), downloads on SATA array (/volume1/data/media/youtube). No SSO, no reverse proxy, no Kuma monitor, no Portainer stack — purely a branch-based hand-run test.

Tech Stack: Docker Compose, Synology DSM (Atlantis NAS), SSH to vish@atlantis, git.

Reference spec: docs/superpowers/specs/2026-04-24-pinchflat-design.md


File Structure

Files added to the repo on branch feat/pinchflat:

  • hosts/synology/atlantis/pinchflat/docker-compose.yml — the single-service compose definition
  • docs/services/individual/pinchflat.md — brief stub (purpose, URL, test status), following repo convention at docs/services/individual/<service>.md

Non-tracked host state created on Atlantis:

  • /volume2/metadata/docker2/pinchflat/config/ — persistent SQLite + YAML config
  • /volume1/data/media/youtube/ — download target folder
  • /volume1/homes/vish/pinchflat-test/ — throwaway working copy of the branch

No modifications to existing files. No Portainer stack registration.


Task 1: Pre-flight checks on Atlantis

Files: None (verification only)

  • Step 1: Confirm port 8945 is free on Atlantis

Run:

ssh vish@atlantis 'ss -tlnp 2>/dev/null | grep -E ":8945\b" || echo "port 8945 free"'

Expected: port 8945 free

If a process is listening on 8945, stop and re-plan — pick a different host port (e.g. 8946) and update the compose file in Task 2 accordingly.

  • Step 2: Confirm media root exists and ownership convention

Run:

ssh vish@atlantis 'ls -ld /volume1/data/media /volume2/metadata/docker2'

Expected output includes two directories owned by a user account, with /volume1/data/media containing existing media subfolders (movies, tv, anime, etc.).

  • Step 3: Confirm Docker daemon is running on Atlantis

Run:

ssh vish@atlantis 'docker ps --format "table {{.Names}}\t{{.Status}}" | head -5'

Expected: a table listing at least a handful of running containers (plex, sonarr, etc.). If Docker isn't responding, stop and investigate before proceeding.


Task 2: Create feature branch and compose file

Files:

  • Create: hosts/synology/atlantis/pinchflat/docker-compose.yml

  • Step 1: Create branch off main

Run from the repo root /home/homelab/organized/repos/homelab:

git checkout main && git pull --ff-only && git checkout -b feat/pinchflat

Expected: branch feat/pinchflat checked out, working tree clean except for the pre-existing untracked items (.secrets.baseline modifications, data/expenses.csv, .superpowers/, backups/).

  • Step 2: Create the compose directory

Run:

mkdir -p hosts/synology/atlantis/pinchflat
  • Step 3: Write hosts/synology/atlantis/pinchflat/docker-compose.yml

Full file contents:

# Pinchflat - YouTube auto-archiver (test deployment)
# Port: 8945
# Docs: https://github.com/kieraneglin/pinchflat
# Scope: lightweight evaluation on Atlantis. No SSO, no reverse proxy, no Kuma.
# See: docs/superpowers/specs/2026-04-24-pinchflat-design.md
version: "3.8"

services:
  pinchflat:
    image: ghcr.io/kieraneglin/pinchflat:latest
    container_name: pinchflat

    environment:
      - PUID=1029
      - PGID=100
      - TZ=America/Los_Angeles
      - UMASK=022

    ports:
      - "8945:8945"

    volumes:
      - /volume2/metadata/docker2/pinchflat/config:/config
      - /volume1/data/media/youtube:/downloads

    healthcheck:
      test: ["CMD-SHELL", "wget -qO /dev/null http://127.0.0.1:8945/ 2>/dev/null || exit 1"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 30s

    security_opt:
      - no-new-privileges:true

    restart: unless-stopped
  • Step 4: Validate the YAML

Run from the repo root:

python3 -c "import yaml; yaml.safe_load(open('hosts/synology/atlantis/pinchflat/docker-compose.yml'))" && echo "YAML OK"

Expected: YAML OK

If parsing fails, fix the indentation/syntax before proceeding. Do not use sed to edit — re-run the Write step with the full corrected file.


Task 3: Write the docs stub

Files:

  • Create: docs/services/individual/pinchflat.md

  • Step 1: Write the doc stub

Full file contents:

# Pinchflat

YouTube channel auto-archiver. Subscribes to channels, polls for new uploads, downloads via yt-dlp, stores locally with metadata/subtitles/chapters.

## Status

**Test deployment as of 2026-04-24.** Running on Atlantis via hand-run `docker compose` on branch `feat/pinchflat`. Not yet registered in Portainer, not yet behind Authentik / NPM / Kuma.

See `docs/superpowers/specs/2026-04-24-pinchflat-design.md` for design rationale and promotion path.

## Access

- **Web UI:** http://192.168.0.200:8945 (LAN only)
- **Host:** Atlantis
- **Image:** `ghcr.io/kieraneglin/pinchflat:latest`

## Paths on Atlantis

- **Compose:** `/volume1/homes/vish/pinchflat-test/hosts/synology/atlantis/pinchflat/docker-compose.yml` (during test)
- **Config:** `/volume2/metadata/docker2/pinchflat/config`
- **Downloads:** `/volume1/data/media/youtube/<Channel>/<YYYY-MM-DD> - <Title>.mkv`

## Runtime defaults (configure in web UI on first launch)

- Output template: `/downloads/{{ source_custom_name }}/{{ upload_yyyy_mm_dd }} - {{ title }}.{{ ext }}`
- Resolution cap: 2160p (4K)
- Container format: MKV (required for VP9/AV1 4K streams)
- Thumbnails: on
- Subtitles: on
- Chapters: on
- NFO files: off

## Operations

### Start / stop (during test phase)

```bash
ssh vish@atlantis
cd /volume1/homes/vish/pinchflat-test
docker compose up -d     # start
docker compose logs -f   # tail logs
docker compose down      # stop (data preserved)
```

### Full teardown (abandon test)

```bash
ssh vish@atlantis
cd /volume1/homes/vish/pinchflat-test
docker compose down -v
sudo rm -rf /volume2/metadata/docker2/pinchflat /volume1/data/media/youtube
cd ~ && rm -rf /volume1/homes/vish/pinchflat-test
```

Then on the workstation:
```bash
git branch -D feat/pinchflat
git push origin --delete feat/pinchflat   # if pushed
```

## Promotion to production (if keeping)

1. Merge `feat/pinchflat``main`.
2. Register new Portainer GitOps stack pointing at `hosts/synology/atlantis/pinchflat/docker-compose.yml`.
3. Stop the hand-run container, re-up via Portainer.
4. Add NPM proxy host `pinchflat.vish.gg` + Authentik proxy provider.
5. Add Kuma HTTP monitor against `http://192.168.0.200:8945`.
6. Pin the image to a specific digest instead of `:latest`.
7. Expand this doc with operational runbook (channel subscription process, troubleshooting, log locations).
  • Step 2: Verify the doc renders as expected Markdown

Run:

head -5 docs/services/individual/pinchflat.md && echo "---" && wc -l docs/services/individual/pinchflat.md

Expected: the first line is # Pinchflat, line count is around 6070.


Task 4: Commit and push the branch

Files: Both files from Task 2 and Task 3.

  • Step 1: Stage the two new files only

Explicitly avoid staging the pre-existing dirty working-tree items (.secrets.baseline, data/expenses.csv, .superpowers/, backups/).

Run:

git add hosts/synology/atlantis/pinchflat/docker-compose.yml docs/services/individual/pinchflat.md
git status --short

Expected: only the two new files appear in the "to be committed" section. Other items remain in the unstaged/untracked section untouched.

  • Step 2: Commit

Run:

git commit -m "feat(pinchflat): add test deployment compose + docs stub

Hand-run docker compose on Atlantis port 8945 for evaluating Pinchflat as a
YouTube auto-archiver. No SSO/NPM/Kuma/Portainer during the test phase.

See docs/superpowers/specs/2026-04-24-pinchflat-design.md for design and
promotion path."

Expected: a commit hook chain runs (trim whitespace, check yaml, yamllint, docker-compose syntax check, detect-secrets). All should pass. Per CLAUDE.md, never add Co-Authored-By lines.

If yamllint fails on the new compose file, read the error, fix the compose file inline with Edit (not sed), re-validate per Task 2 Step 4, then git add + git commit --amend --no-edit is acceptable here since the commit has not been pushed yet.

  • Step 3: Push the branch

Run:

git push -u origin feat/pinchflat

Expected: branch published to Gitea. Output shows tracking established.


Task 5: Deploy on Atlantis

Files: None in the repo; creates host directories and launches the container.

  • Step 1: Create the config and download directories with correct ownership

Pinchflat runs as PUID=1029 PGID=100 (Synology dockerlimited:users). Pre-create the dirs so there's no first-boot permission confusion.

Run:

ssh vish@atlantis 'sudo mkdir -p /volume2/metadata/docker2/pinchflat/config /volume1/data/media/youtube && sudo chown -R 1029:100 /volume2/metadata/docker2/pinchflat /volume1/data/media/youtube && ls -ld /volume2/metadata/docker2/pinchflat /volume1/data/media/youtube'

Expected: both directories exist and are owned by dockerlimited:users (which may display as 1029:users depending on DSM's /etc/passwd).

  • Step 2: Clone the branch to a throwaway working copy on Atlantis

Run:

ssh vish@atlantis 'mkdir -p /volume1/homes/vish/pinchflat-test && cd /volume1/homes/vish/pinchflat-test && git clone --branch feat/pinchflat --depth 1 https://git.vish.gg/vish/homelab.git . && ls hosts/synology/atlantis/pinchflat/'

Expected: a single docker-compose.yml listed under the pinchflat directory. If the git clone fails (auth or network), fall back to scp of the single compose file:

scp hosts/synology/atlantis/pinchflat/docker-compose.yml vish@atlantis:/volume1/homes/vish/pinchflat-test/docker-compose.yml

(in which case the docker compose commands in later steps should be run directly from /volume1/homes/vish/pinchflat-test/ rather than the nested path)

  • Step 3: Pull the image

Run:

ssh vish@atlantis 'cd /volume1/homes/vish/pinchflat-test/hosts/synology/atlantis/pinchflat && docker compose pull'

Expected: image ghcr.io/kieraneglin/pinchflat:latest pulls successfully. If pull fails on auth (ghcr.io rate-limit), add docker login ghcr.io with a PAT and retry.

  • Step 4: Start the container

Run:

ssh vish@atlantis 'cd /volume1/homes/vish/pinchflat-test/hosts/synology/atlantis/pinchflat && docker compose up -d'

Expected: Container pinchflat Started. No errors.

  • Step 5: Verify container status

Run:

ssh vish@atlantis 'docker ps --filter name=pinchflat --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"'

Expected: single row showing pinchflat Up N seconds (health: starting) 0.0.0.0:8945->8945/tcp, :::8945->8945/tcp. Wait ~60 seconds after the first up so the healthcheck can transition from starting to healthy.


Task 6: Smoke test

Files: None (evaluation only)

  • Step 1: Confirm the web UI responds

Run from the workstation:

curl -s -o /dev/null -w "%{http_code}\n" http://192.168.0.200:8945/

Expected: 200 (Pinchflat serves its dashboard at /). If you get 000 (connection refused), wait 30 seconds and retry — the app takes a moment to bind after container start.

  • Step 2: Tail logs for any obvious errors

Run:

ssh vish@atlantis 'docker logs pinchflat --tail 50 2>&1 | grep -iE "error|fatal|panic" | head -20 || echo "no errors in recent logs"'

Expected: no errors in recent logs, or if any errors appear, assess whether they're benign (e.g. migration notices) vs blocking.

  • Step 3: Check container healthcheck transitioned to healthy

Run:

ssh vish@atlantis 'docker inspect pinchflat --format "{{.State.Health.Status}}"'

Expected: healthy. If still starting after 2 minutes, check logs for why the web server isn't responding. If unhealthy, something is wrong — stop and debug before handing off.

  • Step 4: Confirm config and download volumes wrote correctly

Run:

ssh vish@atlantis 'ls -la /volume2/metadata/docker2/pinchflat/config/ | head -10 && echo "---" && ls -la /volume1/data/media/youtube/'

Expected: the config dir now contains Pinchflat's initial files (e.g. database file, YAML config). The downloads dir may still be empty — that's fine, it only populates after the user subscribes to a channel.

  • Step 5: Hand off to user for UI walk-through

Report to the user:

  • URL: http://192.168.0.200:8945
  • Container state: running + healthy
  • Next step is user-driven: open the URL, walk through first-run setup, apply the defaults from §4 of the spec (output template, 4K cap, MKV container, thumbnails/subtitles/chapters on, NFO off), subscribe to 25 test channels.

Do NOT mark the plan as complete at this step. The user drives the evaluation window; the plan is complete when the user makes the keep/drop decision.


Task 7 (deferred): Keep-or-drop decision

Trigger: User signals "keep" or "drop" after evaluation window.

If KEEP:

  • Open follow-up plan for promotion (register Portainer GitOps stack, add NPM + Authentik + Kuma, pin image digest, expand docs).
  • Out of scope for this plan.

If DROP:

  • Run the teardown block from docs/services/individual/pinchflat.md (Full teardown section).
  • Delete the remote branch: git push origin --delete feat/pinchflat.
  • Delete the local branch: git branch -D feat/pinchflat.
  • Optionally delete the two repo files with a follow-up commit on main, or leave the branch deleted and nothing ever landed on main — either is fine.

Self-review notes

  • Spec coverage: Every spec section (architecture, components, storage, runtime defaults, deployment workflow, error handling, testing, rollback) maps to a task or is explicitly deferred. ✓
  • Placeholder scan: No TBDs. No "similar to Task N" shortcuts. Every compose field and command is literal. ✓
  • Type consistency: Paths match across tasks (/volume1/data/media/youtube, /volume2/metadata/docker2/pinchflat/config, /volume1/homes/vish/pinchflat-test, port 8945). Ownership 1029:100 consistent. ✓
  • Known fuzziness: "60-70 lines" for the doc stub is approximate; the exact line count depends on final whitespace. Not a blocker.