# План реализации проекта "Домовой" Документ представляет собой пошаговый план разработки. Каждая итерация — самостоятельный кусок функционала, который можно реализовать, протестировать и запустить независимо. После каждой итерации запускается scripts/check.sh. Без успешного smoke test итерация не считается завершённой. --- ## Итерация 1. Каркас приложения Цель: приложение открывается, есть логин, база, миграции, layout. Что делаем: - Slim 4 приложение с маршрутами - Docker Compose: app (PHP 8.3) + MariaDB - .env.example со всеми переменными - Phinx: конфигурация, миграция users - Регистрация первого пользователя (CLI setup) - Логин/Logout с сессиями - Bootstrap layout с левым меню и верхней панелью - Dashboard с заглушками счётчиков - scripts/check.sh с базовыми проверками Файлы: composer.json phinx.php docker-compose.yml docker/Dockerfile .env.example public/index.php public/assets/css/app.css app/Controllers/AuthController.php app/Controllers/DashboardController.php app/Controllers/SetupController.php app/Middleware/AuthMiddleware.php app/Repositories/UserRepository.php app/Services/AuthService.php templates/layout.php templates/auth/login.php templates/dashboard/index.php migrations/20250526000001CreateUsers.php bin/console scripts/check.sh README.md Проверка: docker compose up -d --build docker compose exec app php vendor/bin/phinx migrate docker compose exec app php bin/console setup:user curl -I http://localhost:8080/login # Открыть в браузере, залогиниться, увидеть dashboard --- ## Итерация 2. Сканирование сети Цель: добавить диапазон, запустить scan, увидеть найденные хосты. Что делаем: - Миграции: network_ranges, scan_jobs, discovered_hosts - NetworkRanges CRUD (вкладка Discovery) - NetworkScanner: ping sweep + TCP connect + ARP + reverse DNS - CLI worker bin/run-scan-worker.php - Страница Discovery: таблица discovered_hosts - htmx для запуска scan и обновления статуса scan_jobs - Audit log для действий сканрования Файлы (новые/изменённые): migrations/20250526000002CreateNetworkRanges.php migrations/20250526000003CreateScanJobs.php migrations/20250526000004CreateDiscoveredHosts.php migrations/20250526000005CreateAuditLog.php app/Controllers/DiscoveryController.php app/Controllers/NetworkRangeController.php app/Services/Discovery/NetworkScanner.php app/Services/Discovery/PingScanner.php app/Services/Discovery/TcpPortScanner.php app/Services/Discovery/ArpTableReader.php app/Services/Discovery/HostFingerprintService.php app/Services/Jobs/ScanJobRunner.php app/Services/Jobs/JobQueue.php app/Repositories/ScanJobRepository.php app/Repositories/DiscoveredHostRepository.php app/Repositories/NetworkRangeRepository.php app/Repositories/AuditLogRepository.php templates/discovery/index.php templates/discovery/ranges.php templates/discovery/hosts.php bin/run-scan-worker.php Проверка: # Добавить диапазон 192.168.1.0/24 # Запустить scan через UI # В другом терминале: docker compose exec app php bin/run-scan-worker.php # Увидеть найденные хосты в таблице Discovery --- ## Итерация 3. Инвентарь устройств Цель: создавать карточки устройств руками и из найденных хостов. Что делаем: - Миграция devices - Devices CRUD (список, создание, редактирование, карточка) - Создание device из discovered_host (кнопка "Создать устройство") - Игнорирование discovered_host (кнопка "Игнорировать") - Карточка устройства со всеми полями - Merge suggestions (по MAC → высокая, hostname → средняя, IP → низкая) - Обновление Dashboard: реальные счётчики устройств Файлы (новые/изменённые): migrations/...CreateDevices.php app/Controllers/DeviceController.php app/Services/Inventory/DeviceService.php app/Services/Inventory/MergeSuggestionService.php app/Repositories/DeviceRepository.php templates/devices/index.php templates/devices/create.php templates/devices/edit.php templates/devices/show.php templates/dashboard/index.php (обновить счётчики) Проверка: # Создать устройство вручную # Создать устройство из discovered_host # Открыть карточку, проверить все поля # Проверить merge suggestions --- ## Итерация 4. SSH-доступы Цель: добавить SSH-доступ к устройству, проверить подключение. --- ## Корректирующая итерация 3.1. Стабилизация Discovery и Inventory Цель: привести уже заявленные итерации 2-3 к рабочему состоянию перед переходом к SSH-доступам и deep scan. Эта итерация обязательна, потому что часть функциональности сейчас реализована как каркас, но не выполняет пользовательский сценарий полностью. Что исправляем: - Worker должен реально выполнять `network_discovery`, а не только менять статус scan job на `done`. - `scan_jobs.network_range_id` должен использоваться worker-ом для выбора конкретного диапазона. - `NetworkScanner` должен корректно перечислять IPv4 CIDR: - `/32` — один IP; - `/31` — оба адреса; - `/30` и меньше — только usable host addresses без network/broadcast; - диапазоны крупнее `/24` для MVP запрещаются или явно ограничиваются, чтобы случайно не запустить огромный scan. - `discovered_hosts.scan_job_id` должен заполняться найденными хостами. - Повторное обнаружение того же IP/MAC не должно плодить неуправляемые дубликаты без обновления `last_seen`. - Добавление network range должно запрещать публичные IPv4-сети по умолчанию. Разрешены только private/local ranges из ТЗ: `10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`, link-local, loopback для локального smoke test. - Discovery UI должен показывать ошибку scan job, если worker не смог выполнить задачу. - Merge suggestions должны быть либо доведены до рабочего UI, либо явно исключены из статуса "готово" итерации 3. - Ссылки в меню на ещё не реализованные разделы (`/services`, `/documents`, `/settings`) не должны выглядеть как готовая функциональность. - `scripts/check.sh` должен проверять не только открытие `/login`, но и smoke path: миграции, создание пользователя, добавление range, создание scan job, запуск worker-а хотя бы на `/32` loopback/private IP. Файлы (ожидаемо новые/изменённые): app/Services/Jobs/ScanJobRunner.php app/Services/Discovery/NetworkScanner.php app/Controllers/NetworkRangeController.php app/Repositories/DiscoveredHostRepository.php app/Repositories/NetworkRangeRepository.php app/Repositories/AuditLogRepository.php templates/discovery/index.php templates/layout.php scripts/check.sh tests/ Проверка: vendor/bin/phpunit composer validate --no-check-publish find app public bin tests -name "*.php" -print0 | xargs -0 -n1 php -l ./scripts/check.sh Критерий готовности: # Добавить private range, например 127.0.0.1/32 или 192.168.1.1/32 # Запустить scan через UI docker compose exec app php bin/run-scan-worker.php # scan_job получает done/failed с понятной причиной # при успешном обнаружении discovered_hosts содержит scan_job_id # публичный range вроде 8.8.8.8/32 не принимается без отдельного разрешения --- ## Итерация 4. SSH-доступы к устройствам Цель: хранить SSH-доступы к устройству, не показывать секреты в UI и уметь проверить подключение. Что делаем: - Миграция credentials - Credentials CRUD (только SSH для MVP) - Шифрование секретов (defuse/php-encryption) - Тест подключения через phpseclib - Блок доступов на карточке устройства - CredentialVault для шифрования/дешифрования - Сохранение результата теста (last_test_status, last_test_at) Файлы (новые/изменённые): migrations/...CreateCredentials.php app/Controllers/CredentialController.php app/Services/Security/CredentialVault.php app/Services/Ssh/SshCredentialTester.php app/Repositories/CredentialRepository.php templates/devices/show.php (добавить блок доступов) .env.example (добавить ENCRYPTION_KEY) tests/Security/CredentialVaultTest.php tests/Repositories/CredentialRepositoryTest.php tests/Services/SshCredentialTesterTest.php Проверка: docker compose exec app php vendor/bin/phinx migrate # Добавить SSH-доступ к устройству # Нажать "Тест" — увидеть результат подключения # Секрет не отображается в UI открытым текстом # В базе хранится зашифрованное значение --- ## Итерация 5. Deep scan Linux-хоста Цель: по SSH собрать read-only информацию с Linux-хоста. Что делаем: - Миграции: host_scans - LinuxHostScanner: hostname, os, ip, ports, disk, systemd, cron - CommandWhitelist с массивами аргументов - SshClientFactory с таймаутами (connect=5s, auth=10s, cmd=8s) - Сохранение raw JSON в storage/scans/ - Summary в host_scans.summary_json - Кнопка "Глубокий скан" на карточке устройства - Страница результата скана - Worker поддерживает тип host_deep_scan Файлы (новые/изменённые): migrations/...CreateHostScans.php app/Controllers/HostScanController.php app/Services/HostScan/LinuxHostScanner.php app/Services/HostScan/CommandWhitelist.php app/Repositories/HostScanRepository.php templates/host_scans/show.php templates/devices/show.php (кнопка deep scan) bin/run-scan-worker.php (поддержка host_deep_scan) config/scan_commands.php (CommandWhitelist mapping) .env.example (добавить SSH_* таймауты) Проверка: # Добавить SSH-доступ к устройству # Нажать "Глубокий скан" # В другом терминале: docker compose exec app php bin/run-scan-worker.php # Увидеть результат: hostname, os, порты, диски, systemd, cron # raw JSON сохранён в storage/scans/ --- ## Итерация 6. Docker scan + detected_services Цель: найти контейнеры и создать предложения сервисов. Что делаем: - Миграция detected_services - DockerScanner: docker ps, inspect, volumes, networks - SecretMasker для env-переменных (PASSWORD, TOKEN, KEY...) - Создание detected_services kind=docker_container - Страница "Найденные сервисы" (detected services) - Действия: Создать сервис, Объединить, Игнорировать, Детали - Host scan обогащается docker-сканом Файлы (новые/изменённые): migrations/...CreateDetectedServices.php app/Controllers/DetectedServiceController.php app/Services/HostScan/DockerScanner.php app/Services/Security/SecretMasker.php app/Repositories/DetectedServiceRepository.php templates/services/detected.php templates/services/_detected_row.php app/Services/HostScan/LinuxHostScanner.php (добавить docker) Проверка: # Запустить deep scan на хосте с Docker # Увидеть detected_services с kind=docker_container # Секретные env-переменные замаскированы # Страница найденных сервисов показывает таблицу --- ## Итерация 7. Создание сервисов + связи Цель: превратить найденный сервис в карточку с отношениями. Что делаем: - Миграции: services, service_endpoints, relations - Service CRUD - Создание service из detected_service (с выбором типа) - Автоматическое создание service_endpoints (port, url) - Создание связей (Device runs_on Service, Service exposes Endpoint) - Карточка сервиса - Обновление detected_service.status = accepted Файлы (новые/изменённые): migrations/...CreateServices.php migrations/...CreateServiceEndpoints.php migrations/...CreateRelations.php app/Controllers/ServiceController.php app/Services/Inventory/ServiceInventoryService.php app/Services/Inventory/RelationService.php app/Repositories/ServiceRepository.php app/Repositories/ServiceEndpointRepository.php app/Repositories/RelationRepository.php templates/services/index.php templates/services/create.php templates/services/show.php templates/services/detected.php (кнопка "Создать сервис") Проверка: # Нажать "Создать сервис" на detected_service # Выбрать тип (web_app, database...) # Сервис создан, endpoint-ы созданы, связи созданы # detected_service.status = accepted # Карточка сервиса показывает связи --- ## Итерация 8. Nginx/Cron/Backup сканеры Цель: находить nginx vhost-ы, cron-задачи, backup-подсказки. Что делаем: - NginxScanner: парсинг server_name, listen, proxy_pass, root - CronScanner: crontab -l, /etc/crontab, /etc/cron.d/* - BackupHintScanner: поиск rsync, borg, restic, tar, mysqldump... - detected_services kind=nginx_vhost, cron_job, backup_hint - Миграция domains - Создание domain suggestions из nginx server_name - Host scan обогащается nginx/cron/backup сканами Файлы (новые/изменённые): migrations/...CreateDomains.php app/Services/HostScan/NginxScanner.php app/Services/HostScan/CronScanner.php app/Services/HostScan/BackupHintScanner.php app/Repositories/DomainRepository.php templates/host_scans/show.php (добавить nginx/cron/backup) templates/devices/show.php (найденные сервисы) Проверка: # Deep scan на хосте с nginx + cron + backup scripts # Увидеть detected_services: nginx_vhost, cron_job, backup_hint # Формулировка backup_hint: "Похоже на backup job" # Domains созданы из nginx server_name # Приватные ключи сертификатов НЕ прочитаны --- ## Итерация 9. Риски + Dashboard + История Цель: предупреждения на dashboard, история сканов, diff. Что делаем: - RiskAnalyzer: сервис без backup, публичный endpoint, SSH открыт, нет свежего scan, TLS истекает - Dashboard: реальные предупреждения - История scan jobs - DiffAnalyzer: новые/пропавшие хосты, порты, контейнеры между сканами - DiffAnalyzer: новые/пропавшие хосты, порты, контейнеры между сканами - Documents CRUD (заметки/runbooks) Файлы (новые/изменённые): migrations/...CreateDocuments.php app/Controllers/DocumentController.php app/Controllers/ScanHistoryController.php app/Controllers/DiffController.php app/Services/Analysis/RiskAnalyzer.php app/Services/Analysis/DiffAnalyzer.php app/Repositories/DocumentRepository.php templates/dashboard/index.php (real warnings) templates/scans/history.php templates/scans/diff.php templates/documents/index.php templates/documents/create.php Проверка: # Dashboard показывает предупреждения ( service без backup и т.д. ) # История scan jobs с статусами # Diff между двумя сканами: новые/пропавшие хосты # Документы: создать runbook, привязать к устройству --- ## Итерация 10. Полировка + документация Цель: финальная доводка проекта. Что делаем: - Полный audit log для всех действий - Документация в README полностью - Скриншоты в README (опционально) - Проверка всех security-правил - scripts/check.sh обновлён под все итерации - Финальный smoke test: весь цикл scan → device → service --- ## Сводная таблица Итерация | Что делаем | Зависимости 1 | Каркас: Slim, Docker, auth, layout | — 2 | Network scan | 1 3 | Devices CRUD | 1 4 | SSH credentials | 1, 3 5 | Deep scan Linux | 1, 3, 4 6 | Docker scan + detected_services | 1, 5 7 | Services + relations | 1, 6 8 | Nginx/Cron/Backup scanners | 1, 5, 7 9 | Risks + Dashboard + History + Docs | 1-8 10 | Полировка + README | 1-9 --- ## Правила для каждой итерации 1. Не переходить к следующей, пока check.sh не прошёл. 2. Показывать реальный вывод smoke test, а не "готово". 3. Маленькие атомарные коммиты. 4. Контроллеры — только роутинг, бизнес-логика в Services. 5. SQL только в Repository-классах. 6. Shell-команды только через CommandWhitelist (массивы аргументов). 7. Никакого произвольного shell executor из UI. 8. Никакого "чистый JS потом допишем" — весь JS заявлен заранее. --- ## Порядок выдачи задания owl-alpha Рекомендуется выдавать по одной итерации: Шаг 1: "Прочитай ТЗ и PLAN.md. Сделай Итерацию 1." Шаг 2: "Прочитай текущий код. Сделай Итерацию 2 по PLAN.md." ... После каждой итерации — ревью, smoke test, фикс замечаний, и только потом следующая итерация. Никогда не давай больше одной итерации за раз.