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
16 KiB
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-vpspve_nodes— cloud-pc, mini-pclxc_infra— все LXC (13 шт.)monitoring_server— monitoringmonitoring_exporters— хосты с Node Exportermonitoring_smart_exporters— cloud-pc, mini-pcvpn_openvpn— ru-vps, ovpn-minishell_hosts— ru-vps, cloud-pc, mini-pc, hermes-aiservers— все управляемые хосты
Общие переменные живут в 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
.envfiles (in.gitignore), Ansible vault, or runtime prompt. - Store
.env.exampletemplates in repo. - Keep: passwords, tokens, private keys, PBS secrets, TLS keys outside repo.
Workflow
- Read Obsidian — understand context, constraints, prior decisions.
- Make change — edit/add role/playbook in
ansible/. - Test safe — run smallest applicable check/playbook.
- 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 сами разберут шаги.