Files
infra/AGENTS.md
T
DmitryandClaude Opus 5 7700ed5a88
lint / yamllint + ansible-lint + syntax-check (push) Canceled after 0s
Sync documentation with the actual infrastructure
The host table listed 9 hosts against 16 in the inventory, the role tree
did not match roles/, and the documented setup path used a venv that no
longer works.

Describe the current entry points instead: nix develop, make, and the
ssh_config include that makes `ssh gitea` work by hand. Point at
`make docs` as the way to regenerate the host table rather than editing
it, since that is what drifted.

Also record what is deliberately incomplete: lxc_docker_host and
compose_service exist but are not wired into any playbook, and LXC
creation is still split between direct pct create over SSH and the
pve_lxc API role.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GTocXkGUUazHdKKd3r9k71
2026-08-26 22:10:38 +03:00

16 KiB
Raw Blame History

AGENTS.md

Project Summary

HomeLab infras is a GitOps-like repository for managing home infrastructure through Ansible.

  • Management: ansible/ (playbooks, roles, inventory)
  • Documentation: Obsidian vault /home/ada/Documents/Vaults/SecondBrain/02 Projects/HomeLab/
  • Active infrastructure: Proxmox VE cluster (cloud-pc, mini-pc), PBS, OpenVPN, ZeroTier
  • Archive: archive/2026-07-proxmox-migration/ (historical NixOS/Docker configs, reference only)

Design Goal

Ansible-first infrastructure: HomeLab configuration is managed through Ansible inventory, roles and playbooks. Direct server changes are allowed only for break-glass recovery or read-only diagnostics; afterwards the intended state must be captured in Ansible.

Obsidian Integration

Project documentation lives in /home/ada/Documents/Vaults/SecondBrain/02 Projects/HomeLab/. Use it as wiki:

  • Read before making infra changes — context, decisions, constraints.
  • Update after making changes — document non-obvious decisions, new patterns, lessons learned.
  • Key files: HomeLab.md, Notes/Текущее состояние HomeLab после миграции на Proxmox.md, Log.md.

When introducing infra changes, update the relevant Obsidian note to keep documentation in sync.

Operating Model

  • User: passwords, SSH access, base network reachability.
  • Agent: infrastructure changes via ansible/ files.
  • Prefer adding/updating a role or playbook over ad-hoc commands.
  • For every new VM, LXC container, or managed host, provision the user's SSH public key by default unless explicitly told otherwise.
  • Run smallest safe Ansible check before broader changes.
  • Do not commit secrets (.env, tokens, keys, real passwords).

Quick Start

Окружение собрано в Nix — venv и системные пакеты не нужны.

nix develop            # или один раз: direnv allow

# Один раз на клон: galaxy-коллекции
ansible-galaxy collection install -r ansible/requirements.yml -p ansible/collections

Дальше всё управление идёт через make из каталога ansible/:

cd ansible
make help              # список всех целей — начинать отсюда
make check             # связность и ожидаемые IP
make status            # сводное состояние всей инфраструктуры (read-only)
make lint              # ansible-lint + yamllint
make docs              # актуальная таблица хостов из inventory

make deploy-gitea      # playbooks/pve-gitea.yml, .env подхватывается сам
make dry-gitea         # то же в режиме --check --diff
make update-gitea      # бэкап -> обновление -> health-check

make без аргументов печатает help. Опасные цели (mihomo-harden, monitoring, update-all) требуют явного CONFIRM=1. Дополнительные флаги — через EXTRA:

make status EXTRA="--limit '!gyro'"

gyro использует шифрованный host_vars/gyro/vault.yml, поэтому для него нужен --ask-vault-pass (цель make gyro добавляет флаг сама).

SSH руками

Транспорт описан в ansible/ssh_config — один источник правды и для Ansible, и для терминала. Чтобы заработал ssh gitea, добавь в ~/.ssh/config:

Include /home/ada/Documents/Projects/HomeLab/infras/ansible/ssh_config

SSH берёт первое совпадение: Include в начале файла — побеждают настройки репозитория, в конце — личные записи из ~/.ssh/config.d/. На Ansible порядок не влияет, он ходит с явным -F.

Active Infrastructure

Каноничный источник — ansible/inventory/hosts.yml и реестр сервисов ansible/inventory/group_vars/all/services.yml. Актуальную таблицу всегда можно получить командой make docs, поэтому здесь — только опорная картина.

Nodes

Name Role LAN IP Notes
ru-vps Public VPS, JumpHost, qdevice, OpenVPN server, Caddy 157.22.231.198:3422 OpenVPN 10.78.0.1
cloud-pc Proxmox VE node, storage 192.168.1.5 Main node
mini-pc Proxmox VE node 192.168.1.10 Secondary node
pbs Proxmox Backup Server LXC (CT 120) 192.168.1.20 Прямой доступ, без ProxyJump
ovpn-mini OpenVPN gateway LXC (CT 132) 192.168.1.23 OpenVPN 10.78.0.2, без ProxyJump
vaultwarden Vaultwarden LXC (CT 140) 192.168.1.24 pass.ada-dev.ru
gitea Gitea LXC (CT 141) 192.168.1.25 git.ada-dev.ru, SSH :2222
memoir-bot Telegram memoir bot LXC (CT 142) 192.168.1.26 образ собирается локально
mihomo Local proxy/UI LXC (CT 143) 192.168.1.27 UI :8080, API :9090, proxy :7890/:7891
adguard AdGuard Home LXC (CT 144) 192.168.1.28 DNS :53, UI :3000
docker-test Песочница/кандидат в CI-раннеры (CT 145) 192.168.1.29
monitoring Monitoring LXC (CT 146) 192.168.1.30 Uptime Kuma активен, Prometheus заморожен
hermes-ai AI-хост (CT 147) 192.168.1.31 ходит наружу через mihomo
emergency-bot Telegram-бот break-glass доступа (CT 148) 192.168.1.32
grimmory Grimmory + MariaDB (CT 149) 192.168.1.34 books.ada-dev.ru
gyro Investment allocator (CT 150) 192.168.1.35 vault-переменные, outbound-only

Inventory Groups

  • homelab — ru-vps
  • pve_nodes — cloud-pc, mini-pc
  • lxc_infra — все LXC (13 шт.)
  • monitoring_server — monitoring
  • monitoring_exporters — хосты с Node Exporter
  • monitoring_smart_exporters — cloud-pc, mini-pc
  • vpn_openvpn — ru-vps, ovpn-mini
  • shell_hosts — ru-vps, cloud-pc, mini-pc, hermes-ai
  • servers — все управляемые хосты

Общие переменные живут в inventory/group_vars/, а не в hosts.yml: group_vars/all/main.yml (сеть, доступ, OpenVPN), group_vars/lxc_infra/main.yml (LXC ходят под root без sudo), group_vars/all/services.yml (реестр сервисов).

Transport

  • OpenVPN: ru-vps (10.78.0.1) ↔ ovpn-mini (10.78.0.2), порт 8443/tcp
  • JumpHost: SSH к нодам и LXC через ProxyJump ru-vps; исключения — pbs и ovpn-mini, они доступны напрямую по LAN
  • Всё это описано в ansible/ssh_config; Ansible подключает его через ansible_ssh_common_args в group_vars/all/main.yml (путь считается от inventory_dir, поэтому не зависит от cwd и места клона)

Directory Structure

flake.nix / .envrc           # Nix dev-окружение (ansible, линтеры, python-зависимости)
.ansible-lint / .yamllint    # Конфигурация линтеров
.gitea/workflows/lint.yml    # CI: yamllint + ansible-lint + syntax-check

ansible/
├── Makefile                 # ЕДИНАЯ точка входа для ручного управления (make help)
├── ansible.cfg
├── ssh_config               # Транспорт: ProxyJump, пользователи, ключи
├── scripts/
│   └── gen-inventory-docs.py
├── inventory/
│   ├── hosts.yml            # Только адреса и индивидуальные факты хостов
│   ├── group_vars/
│   │   ├── all/main.yml     # Общие переменные
│   │   ├── all/services.yml # Реестр сервисов homelab_services
│   │   └── lxc_infra/main.yml
│   └── host_vars/gyro/      # main.yml + шифрованный vault.yml
├── playbooks/
│   ├── check.yml            # Связность и ожидаемые IP
│   ├── status.yml           # Read-only сводка по всей инфраструктуре
│   ├── reverse-proxy.yml    # Единый Caddy-плейбук по реестру сервисов
│   ├── pve-*.yml            # Создание LXC (нужен .env)
│   ├── *-update.yml         # Обновления: бэкап -> апдейт -> health-check
│   └── openvpn-*.yml
├── roles/
│   ├── lxc_docker_host/     # LXC под Docker: пакеты, fuse-overlayfs, ufw
│   ├── compose_service/     # compose.yml + systemd-юнит + health-check
│   ├── pve_lxc/             # Создание LXC через Proxmox API
│   ├── backup_audit/        # Аудит PBS и restic
│   ├── monitoring_*/        # Prometheus-стек (заморожен), экспортеры
│   ├── uptime_kuma/         # Активный мониторинг
│   ├── emergency_access/    # Break-glass reverse SSH
│   ├── emergency_bot/       # Telegram-бот break-glass
│   ├── gyro/                # Investment allocator
│   ├── openvpn_gateway/     # OpenVPN транспорт
│   └── bash_config/         # Единый bash-конфиг
└── tasks/

archive/2026-07-proxmox-migration/  # Историческое, только как справка

Роли lxc_docker_host и compose_service созданы, но пока не подключены ни к одному плейбуку — миграция pve-*.yml на них не выполнена. Пример использования и перечень того, что меняется на живом хосте при переходе, — в roles/compose_service/README.md.

Common Playbooks

Предпочитай make — он сам грузит .env и защищает опасные цели.

make check             # связность и ожидаемые IP
make status            # read-only сводка: хосты, юниты, диски, VPN, бэкапы
make deploy-<service>  # playbooks/pve-<service>.yml
make dry-<service>     # то же с --check --diff
make update-<service>  # gitea | vaultwarden | adguard | mihomo | grimmory
make reverse-proxy     # Caddy на ru-vps по реестру сервисов
make backup-audit      # аудит PBS + offsite restic
make openvpn-check     # проверка транспорта ru-vps <-> ovpn-mini
make bash-config       # единый bash-конфиг на shell_hosts
make user-ssh-key      # разложить публичный ключ на все хосты

Обновления всегда идут в порядке: свежий бэкап/аудит -> обновление -> health-check. Образы закреплены по tag@sha256:digest; floating-теги и авто-апдейтеры не используются.

Proxmox API Tasks

Плейбукам pve-*.yml нужны переменные Proxmox API. Держи их в ansible/.env (файл в .gitignore, шаблон — .env.example):

PROXMOX_HOST=<host>
PROXMOX_USER=<user@realm>
PROXMOX_TOKEN_ID=<token_id>
PROXMOX_TOKEN_SECRET=<secret>
PROXMOX_VALIDATE_CERTS=false

make подхватывает .env сам и падает с внятным сообщением, если его нет — руками . ./.env делать не нужно:

make env-check         # проверить, что .env на месте и заполнен
make dry-gitea         # сначала всегда --check --diff
make deploy-gitea

Выпустить токен, если его ещё нет:

make bootstrap-pve-token          # прямо с ноды, нужен sudo
make bootstrap-monitoring-token   # отдельный read-only токен для мониторинга

Secrets Policy

  • Never commit real secrets to git.
  • Use .env files (in .gitignore), Ansible vault, or runtime prompt.
  • Store .env.example templates in repo.
  • Keep: passwords, tokens, private keys, PBS secrets, TLS keys outside repo.

Workflow

  1. Read Obsidian — understand context, constraints, prior decisions.
  2. Make change — edit/add role/playbook in ansible/.
  3. Test safe — run smallest applicable check/playbook.
  4. Update Obsidian — document non-obvious decisions and changes.

Archive Policy

  • archive/2026-07-proxmox-migration/ contains historical NixOS, Docker Compose, GitOps configs.
  • Use archived files only as reference when porting behavior to Ansible.
  • Do not edit archived files for active infrastructure.
  • Restore services via new Ansible roles/playbooks, not by moving old files back.

Token Economy

Принципы минимизации токенов при управлении инфраструктурой.

Delegate Heavy Reading

Когда нужно анализировать большие логи или выводы — делегируй субагенту:

agent("Read this log and summarize key errors")

Субагент использует для:

  • Анализа логов (journalctl, docker logs, PBS logs)
  • Summarization больших файлов
  • Поиска паттернов в выводах
  • Диагностики статусов

Основной агент для:

  • Принятия решений на основе конспекта
  • Сложных рассуждений над findings
  • Изменения кода и архитектуры

Prefer Scripts Over LLM

Для повторяющихся задач — скрипты, а не объяснения:

# Вместо: "проверь статус всех LXC"
ansible/scripts/check-lxc-status.sh

# Вместо: "верифицируй PBS backup jobs"
ansible/scripts/check-pbs-backups.sh

Паттерн: написать один раз → переиспользовать. LLM только пишет или вызывает.

Focused Context — читай только нужное

Вместо чтения всего файла — только релевантные части:

# Вместо всего inventory.yml
Read(file_path, offset=1, limit=50)  # только hosts vars

# Или grep для конкретного хоста
grep("mini-pc", "inventory/hosts.yml")

Tool Limits — ограничивай вывод команд

# Вместо: journalctl -u openvpn (тысячи строк)
journalctl -u openvpn --since "1 hour ago" -n 100

# Вместо: docker logs (весь лог)
docker logs --tail 50 gitea

Всегда ограничивай вывод: --tail, --since, -n, head.

Batch Operations — группируй задачи

Вместо нескольких запусков — один с несколькими role:

# Плохо
ansible-playbook playbooks/bash-config.yml
ansible-playbook playbooks/docker.yml
ansible-playbook playbooks/ufw.yml

# Хорошо — один плейбук
ansible-playbook playbooks/base-setup.yml  # включает bash, docker, ufw

State Caching — не перечитывай статичное

Структура инфры меняется редко. Не перечитывай hosts.yml, если структура не изменилась. Кэшируй в memory стабильные данные: network topology, storage layout.

Structured Output — избегай повторного парсинга

Когда субагент анализирует логи — проси сразу JSON с findings:

{
  "summary": "OpenVPN handshake fails due to certificate expired",
  "findings": ["cert expired 2026-07-01", "client retries every 5s"],
  "relevant_lines": ["Jul 08 10:23:01 TLS auth error"]
}

Работа со структурированным результатом вместо повторного чтения логов.

Declarative Over Imperative

Описывай желаемое состояние, а не шаги:

# Плохо: каждый раз перечислять шаги
"Создай LXC, установи Docker, добавь пользователя..."

# Хорошо: декларативно
lxc_container:
  name: "vaultwarden"
  features: ["docker", "autostart"]
  user: "ansible"

Ansible/Terraform сами разберут шаги.