Streamline AI docs, add skills, enforce symlink-everything policy

- Add .claude/skills/: homelab-context (auto-loads key facts each session),
  homelab-status (/homelab-status command), deploy-config (symlink setup guide)
- Remove AGENTS.md and ai/sessions/: superseded by plan mode + skill system
- Remove 4 obsolete session commands (session-start/list/switch, reload-instructions)
- Rewrite CLAUDE.md: remove duplicate content, enforce symlink policy, clarify sudo pattern
- Trim docs/services.md from 946 to ~230 lines: remove planning-era content,
  keep install steps and current status for migration reference
- Strip stale "sudo cp" deploy header from ssh-honeypot.service (now symlinked to repo)
- Update TODO.md: mark NAS migration and symlink tasks done, add jellyfin upgrade warning
This commit is contained in:
2026-03-01 23:45:18 +01:00
parent 14cc6aa50d
commit c842b4a398
8 changed files with 388 additions and 1127 deletions

250
CLAUDE.md
View File

@@ -4,236 +4,106 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
## Project Overview
This is a personal homelab setup repository documenting the migration from cloud services to self-hosted solutions on a ThinkPad running Arch Linux. The project uses Docker for containerized services behind an Nginx reverse proxy.
Personal homelab on a ThinkPad running Arch Linux. Docker services behind an Nginx reverse proxy. Full context is in `.claude/skills/homelab-context/SKILL.md`.
## Architecture
### Network Infrastructure
- **Domain**: ak-homelab.duckdns.org (DuckDNS dynamic DNS)
- **Static IP**: 192.168.0.100 (dual interface: ethernet enp4s0, WiFi wlp1s0)
- **SSH**: Custom port 2222 for system access
- **Reverse Proxy**: Nginx for path-based routing to services
- **Domain**: ak-homelab.duckdns.org (DuckDNS)
- **Static IP**: 192.168.0.100 (interface: enp4s0)
- **NAS**: 192.168.0.101, mounted at `/mnt/nas/`
- **Reverse Proxy**: Nginx → path-based routing to services
## Configuration Management
All configs are version-controlled in `config/`. Docker compose files are **always symlinked** — never copied:
### Service Architecture
```
Internet → Router → Nginx (80/443) → Services
→ SSH (2222) → System
→ Gitea SSH (2223) → Git operations
/opt/docker/<service>/docker-compose.yml → ~/homelab/config/docker/<service>/docker-compose.yml
```
**Current Services:**
- Gitea Git server: Docker container on port 3000, accessible via `/gitea/` path
- Copyparty file server: Systemd service on port 8082, accessible via `/files/` path with WebDAV support
- Jellyfin media server: Docker container on port 8096, accessible via `/media/` path
- Nginx: Reverse proxy routing to `ak-homelab.duckdns.org/servicename/`
Editing the repo file is editing the live config. Restart the container to apply changes.
### Configuration Management
All configs — Docker, nginx, systemd units, www — are symlinked to this repo. Editing a file in `config/` is editing the live config. Use `/deploy-config` only on a new machine to create symlinks from scratch, or if drift is detected (a real file exists instead of a symlink).
All configurations are version controlled in the `config/` directory:
- `config/docker/gitea/`: Gitea container setup and deployment scripts
- `config/docker/jellyfin/`: Jellyfin media server configuration
- `config/nginx/`: Reverse proxy configurations with SSL and WebDAV support
- `config/scripts/`: Utility scripts for system maintenance
- `~/.config/copyparty/`: Copyparty file server configuration
## Sudo Access
Configuration files include deployment instructions in comments showing target locations (e.g., `/opt/docker/gitea/`, `/etc/nginx/sites-available/`).
A sudoers rule at `/etc/sudoers.d/homelab-scripts` grants NOPASSWD for scripts in `scripts/tmp/`:
```
hoborg ALL=(ALL) NOPASSWD: /bin/bash /home/hoborg/homelab/scripts/tmp/*
```
Scripts generated there can be run with `sudo bash ~/homelab/scripts/tmp/<script>.sh` — no password needed.
Never run sudo commands directly. Always create a script in `scripts/tmp/`.
## Docker Guidelines
- **NAS-mounted volumes** (`/mnt/nas/`): use `USER_UID=1024 USER_GID=100` to match NAS file ownership
- **Local volumes**: use `USER_UID=1000 USER_GID=1000`
- **s6-overlay containers** (Gitea): do NOT set `user:` directive — use `USER_UID`/`USER_GID` env vars only
- **Never copy docker-compose files** — they are symlinked; copying breaks the link and causes config drift
## Key Commands
### Docker Service Management
### Docker
```bash
# Gitea operations (from /opt/docker/gitea/)
docker-compose logs gitea # View logs
docker-compose down # Stop
docker-compose up -d # Start
docker-compose pull && docker-compose up -d # Update
# Jellyfin operations (from /opt/docker/jellyfin/)
docker-compose logs jellyfin # View logs
docker-compose down # Stop
docker-compose up -d # Start
docker-compose pull && docker-compose up -d # Update
# Deploy from repo
sudo cp config/docker/gitea/docker-compose.yml /opt/docker/gitea/
sudo cp config/docker/jellyfin/docker-compose.yml /opt/docker/jellyfin/
cd /opt/docker/<service>
docker compose logs -f # View logs
docker compose restart # Restart
docker compose down && docker compose up -d # Full restart
```
### Copyparty File Server Management
### Nginx
```bash
# Service operations
sudo systemctl status copyparty # Check status
sudo systemctl start copyparty # Start service
sudo systemctl stop copyparty # Stop service
sudo systemctl restart copyparty # Restart (reload config)
# Configuration
sudo cp ~/.config/copyparty/copyparty.conf ~/.config/copyparty/copyparty.conf.backup
# Edit config and restart service to reload
```
### Nginx Operations
```bash
# Deploy configuration
sudo cp config/nginx/homelab.conf /etc/nginx/sites-available/homelab
sudo ln -s /etc/nginx/sites-available/homelab /etc/nginx/sites-enabled/homelab
# Management
sudo nginx -t # Test config
sudo systemctl reload nginx # Reload
sudo systemctl status nginx # Status
sudo systemctl reload nginx # Reload (use restart for major changes)
```
### SSL Certificate Management
### Systemd Services
```bash
sudo certbot --nginx -d ak-homelab.duckdns.org
sudo systemctl enable certbot.timer # Auto-renewal
sudo systemctl status nginx copyparty
sudo systemctl restart <service>
```
## Development Workflow
### Task Tracking
Use `TODO.md` for centralized task management organized by category (Network & Security, Git & Development, System Configuration, etc.). Mark completed items and update status in documentation.
1. Edit files in `config/` (these are the live configs for Docker via symlink)
2. For non-Docker configs: use `/deploy-config` skill or write a `scripts/tmp/` script
3. Update `docs/` if the change affects setup or architecture
4. Update `.claude/skills/homelab-context/SKILL.md` if ports, paths, or rules changed
5. Commit with a clear message
### Configuration Changes
1. Edit files in `config/` directory
2. Test locally when possible
3. Deploy to target locations with sudo commands included in file headers
4. Update documentation status in `docs/services.md`
5. Commit changes with logical separation
## Documentation Structure
### Documentation Structure
- `docs/`: Technical documentation for setup procedures
- `README.md`: Overview and current status
- `TODO.md`: Active task tracking
- Configuration files self-document deployment locations in headers
- `docs/` — Human-readable reference. Keep for migration and troubleshooting.
- `README.md` — Overview and current status
- `TODO.md` — Active task tracking
- `.claude/skills/homelab-context/SKILL.md` — AI-optimized operational facts (keep in sync with reality)
### Network Configuration Notes
- System uses dual ethernet ports (enp3s0f0, enp4s0) - stick to enp4s0 consistently
- Port conflicts: SSH (2222), Gitea SSH (2223), Gitea Web (3000)
- Router forwarding: HTTP (80), HTTPS (443), SSH (2222), Git SSH (2223)
### Service URLs
- **Local access**: http://192.168.0.100/servicename/
- **External access**: https://ak-homelab.duckdns.org/servicename/
- **Gitea SSH**: ssh://git@ak-homelab.duckdns.org:2223
- **Voice Assistant**: http://127.0.0.1:8880 (local TTS server)
- **Copyparty WebDAV**: https://ak-homelab.duckdns.org/files/ (for X-plore, rclone, etc.)
### Voice Assistant Commands
```bash
# Enable voice mode (starts server and configures voice-mode)
./scripts/enable-voice.sh
# Disable voice mode (stops server)
./scripts/disable-voice.sh
# Manual voice server management
cd voice-server
poetry run voice-server # Start server
poetry install # Install dependencies
poetry run pytest # Run tests
# Test TTS directly
curl -X POST "http://127.0.0.1:8880/v1/audio/speech" \
-H "Content-Type: application/json" \
-d '{"input": "Hello world!", "voice": "ryan"}' \
--output test.wav
```
### Disk Usage Analysis Tools
```bash
# System-level disk space overview (recommended for regular checks)
duf # Modern df with colors and progress bars
# Directory size analysis
dust /home/hoborg # Quick visual summary with tree view
dust /home/hoborg --depth 3 # Limit depth for large directories
# Interactive cleanup and detailed analysis
ncdu /home/hoborg # Navigate with arrow keys, delete with 'd'
ncdu /opt/docker # Analyze Docker data usage
ncdu ~ # Scan home directory for large files
# Usage patterns:
# 1. duf - Quick system health check (like htop for disk space)
# 2. dust - Fast overview of directory sizes (like tree with sizes)
# 3. ncdu - Deep dive cleanup and file management (like htop for files)
```
### WebDAV Client Setup
```bash
# X-plore File Manager (Android)
# Server: ak-homelab.duckdns.org
# Path: /files/
# Port: 443 (HTTPS)
# Username: hoborg
# Password: AdminPass2024!
# rclone configuration
rclone config create homelab-webdav webdav \
url=https://ak-homelab.duckdns.org/files/ \
vendor=other \
user=hoborg \
pass=$(rclone obscure "AdminPass2024!")
# Mount with rclone
rclone mount homelab-webdav: ~/homelab-files --daemon
# Test WebDAV
curl -X PROPFIND https://hoborg:AdminPass2024!@ak-homelab.duckdns.org/files/ \
-H "Depth: 1" -H "Content-Type: text/xml"
```
- Always edit the local configs before when possible, and then copy them to the proper location. Instead of editing system files directly (and then losing the config and it won't be in this repo)
- Never run sudo commands, instead create a script in scripts/tmp and ask the user to run it.
For detailed install steps per service, see `docs/services.md`.
## Security Hardening Guidelines
- When working on security hardening, make sure you **understand the service needs** first, to ensure the security doesn't interfre with normal operations (e.g. some services need read/write filesystem access, not just read)
- Some containers (like Gitea with s6-overlay) need root start then privilege drop via USER_UID/USER_GID environment variables
- Understand service needs before applying restrictions (some need rw filesystem access)
- Test each security change individually, not in batches
- Network access patterns matter: SSH Git needs direct access, HTTP can be proxied through localhost
- DO NOT set Docker user: directive for services using s6-overlay init systems (breaks initialization)
- Network access patterns matter: SSH Git needs direct access, HTTP can go through localhost proxy
## Claude Development Guidelines
### CRITICAL: Anti-Pattern Reminders
**BEFORE writing ANY script or solution, Claude MUST:**
1. **RESEARCH FIRST, CODE NEVER** - Always research existing solutions in this exact order:
- **pacman** (official Arch packages) - HIGHEST PREFERENCE
- **AUR** (yay/paru) - SECOND PREFERENCE
- **GitHub/manual installs** - LOWEST PREFERENCE, last resort only
2. **VERIFY SYNTAX BEFORE WRITING** - Never generate scripts with broken syntax:
- Use `--help` to check command syntax BEFORE using it
- Test commands in small parts first
- Never assume argument names or formats
3. **STICK TO THE CHOSEN SOLUTION** - Don't drift away from proven solutions:
- If research finds tool X works, USE tool X
- Don't randomly switch to tool Y mid-implementation
- Finish what you start before considering alternatives
4. **PREFER SIMPLE OVER COMPLEX**:
- Use existing tools rather than writing custom scripts
- Bash for simple tasks, Python only when complexity requires it
- One working solution beats three broken attempts
5. **CHECK PACKAGE REPOSITORIES FIRST**:
- Always check `pacman -Ss` before any manual installation
- Always check `yay -Ss` before downloading random scripts
- Maintained packages > GitHub scripts > custom solutions
1. **RESEARCH FIRST** — Check existing solutions in order: pacman → AUR → GitHub
2. **VERIFY SYNTAX BEFORE WRITING** — Use `--help`, test in parts, never assume argument formats
3. **STICK TO THE CHOSEN SOLUTION** — Don't drift mid-implementation
4. **PREFER SIMPLE OVER COMPLEX** — Existing tools > custom scripts; bash > python for simple tasks
5. **CHECK PACKAGE REPOS FIRST**`pacman -Ss` before any manual installation
### Failure Patterns to Avoid
-Writing broken syntax without testing commands first
- ❌ Switching solutions mid-implementation without reason
-Broken syntax without testing commands first
- ❌ Switching solutions mid-implementation
- ❌ Overcomplicating when simple solutions exist
- ❌ Installing random scripts before checking packages
- ❌ Creating custom tools when proven ones exist
### Success Pattern to Follow
### Success Pattern
- ✅ Research → Choose → Verify syntax → Implement → Test
- ✅ pacman → AUR → GitHub (in that preference order)
- ✅ pacman → AUR → GitHub (preference order)
- ✅ Use proven, maintained tools over custom scripts
- ✅ Test command syntax with `--help` first
- ✅ Stay focused on the chosen solution