# 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 и системные пакеты не нужны. ```bash nix develop # или один раз: direnv allow # Один раз на клон: galaxy-коллекции ansible-galaxy collection install -r ansible/requirements.yml -p ansible/collections ``` Дальше всё управление идёт через `make` из каталога `ansible/`: ```bash 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`: ```bash 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` и защищает опасные цели. ```bash make check # связность и ожидаемые IP make status # read-only сводка: хосты, юниты, диски, VPN, бэкапы make deploy- # playbooks/pve-.yml make dry- # то же с --check --diff make update- # 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`): ```bash PROXMOX_HOST= PROXMOX_USER= PROXMOX_TOKEN_ID= PROXMOX_TOKEN_SECRET= PROXMOX_VALIDATE_CERTS=false ``` `make` подхватывает `.env` сам и падает с внятным сообщением, если его нет — руками `. ./.env` делать не нужно: ```bash make env-check # проверить, что .env на месте и заполнен make dry-gitea # сначала всегда --check --diff make deploy-gitea ``` Выпустить токен, если его ещё нет: ```bash 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 Для повторяющихся задач — скрипты, а не объяснения: ```bash # Вместо: "проверь статус всех LXC" ansible/scripts/check-lxc-status.sh # Вместо: "верифицируй PBS backup jobs" ansible/scripts/check-pbs-backups.sh ``` Паттерн: написать один раз → переиспользовать. LLM только пишет или вызывает. ### Focused Context — читай только нужное Вместо чтения всего файла — только релевантные части: ```python # Вместо всего inventory.yml Read(file_path, offset=1, limit=50) # только hosts vars # Или grep для конкретного хоста grep("mini-pc", "inventory/hosts.yml") ``` ### Tool Limits — ограничивай вывод команд ```bash # Вместо: 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: ```python # Плохо 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: ```json { "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 Описывай желаемое состояние, а не шаги: ```yaml # Плохо: каждый раз перечислять шаги "Создай LXC, установи Docker, добавь пользователя..." # Хорошо: декларативно lxc_container: name: "vaultwarden" features: ["docker", "autostart"] user: "ansible" ``` Ansible/Terraform сами разберут шаги.