# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Commands ```bash # Prerequisites (once, on operator machine) ansible-galaxy collection install community.general community.docker ansible.posix echo "yourpassword" > ~/.vault-password-file && chmod 600 ~/.vault-password-file # First-time server setup (run as root) ansible-playbook playbooks/bootstrap.yml -u root # Idempotent deploy (all subsequent runs) ansible-playbook playbooks/deploy.yml # Edit secrets ansible-vault edit inventory/group_vars/all.vault.yml # Check syntax without connecting ansible-playbook playbooks/deploy.yml --syntax-check # Dry run ansible-playbook playbooks/deploy.yml --check # Run only specific role ansible-playbook playbooks/deploy.yml --tags base ansible-playbook playbooks/deploy.yml --tags docker ansible-playbook playbooks/deploy.yml --tags services ``` ## Architecture **Traffic flow:** Internet → Traefik (ports 80/443, TLS via Let's Encrypt ACME) → services. Ports 80 and 443 are open on the server. **Secrets:** All secrets live in `inventory/group_vars/all.vault.yml` (Ansible Vault, AES-256). The file `all.yml` references them via `"{{ vault_* }}"` aliases. The vault password must exist at `~/.vault-password-file` on the operator machine — this path is in `.gitignore` and never committed. **Roles:** - `base` — OS hardening: UFW (allow SSH + 80 + 443), fail2ban, sshd config, deploy user - `docker` — Docker CE + Compose plugin via official apt repo - `services` — renders Jinja2 templates → `/opt/services/`, then runs `docker compose up` **Templates → server files:** - `roles/services/templates/docker-compose.yml.j2` → `/opt/services/docker-compose.yml` - `roles/services/templates/env.j2` → `/opt/services/.env` (mode 0600) - `roles/services/templates/traefik/traefik.yml.j2` → `/opt/services/traefik/traefik.yml` - `acme.json` created at `/opt/services/traefik/acme.json` (mode 0600, mounted into Traefik) **Docker networks:** - `backend` (internal) — traefik ↔ user-facing services - `forgejo-db` (internal) — forgejo ↔ its postgres - `plane-internal` (internal) — all plane components (api, worker, beat, db, redis, minio) **Adding a new service:** add container to `docker-compose.yml.j2` on the `backend` network with `traefik.enable=true` and `traefik.http.routers.X.tls.certresolver=letsencrypt` labels, add its domain variable to `all.yml`. ## Deployment DNS: add A-records for each subdomain → server IP (or wildcard `*` → IP). Fill `all.vault.yml` → set `domain_base` in `all.yml` → run bootstrap + deploy. Traefik obtains TLS certificates automatically on first request to each domain.