Commit the accumulated infrastructure work that was living only in the working tree: monitoring stack, emergency access/bot, gyro allocator, grimmory, adguard, backup audit and the OpenCode agent definitions. Also ignore Python bytecode, local archives and Nix/direnv artifacts. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GTocXkGUUazHdKKd3r9k71
552 lines
29 KiB
Markdown
552 lines
29 KiB
Markdown
# План развертывания Grimmory
|
||
|
||
## Целевая архитектура
|
||
|
||
- Отдельный unprivileged LXC `grimmory` на `cloud-pc`.
|
||
- Итоговые значения: CTID `149`, IP `192.168.1.34`. Первоначальный `.33` оказался занят после создания LXC.
|
||
- RootFS на storage `data`, первоначально `64 GB` с возможностью расширения.
|
||
- Ресурсы: `2 vCPU`, `4 GB RAM`, `1 GB swap`.
|
||
- Docker с `fuse-overlayfs` внутри LXC.
|
||
- Docker Compose: Grimmory и MariaDB.
|
||
- Публичный адрес: `https://books.ada-dev.ru`.
|
||
- Книги и данные находятся внутри диска LXC.
|
||
- Daily PBS snapshot всего CT.
|
||
- Offsite restic: дамп MariaDB, конфигурация и `/app/data`, но не книги.
|
||
- Образы фиксируются на версиях:
|
||
- `grimmory/grimmory:v3.2.4`;
|
||
- `lscr.io/linuxserver/mariadb:11.4.8`.
|
||
|
||
Размер диска нужно скорректировать до развертывания, если текущая библиотека уже приближается к 40-50 GB.
|
||
|
||
## 1. Предварительные проверки
|
||
|
||
1. Проверить, что CTID `149` свободен:
|
||
- в Proxmox API;
|
||
- в `pct list` на обоих узлах;
|
||
- в PBS среди исторических VMID.
|
||
2. Проверить IP `192.168.1.34`:
|
||
- отсутствие в inventory;
|
||
- отсутствие DHCP reservation;
|
||
- отсутствие ответа на ARP/ping;
|
||
- отсутствие DNS-записи с другим назначением.
|
||
3. Проверить емкость `cloud-pc:data`.
|
||
4. Проверить работоспособность PBS datastore и последнего backup job `cloud-pc`.
|
||
5. Проверить возможность создать DNS-запись `books.ada-dev.ru`.
|
||
6. Зафиксировать ожидаемый объем существующей коллекции и темп роста.
|
||
7. Получить digest образов после выбора версий. В первой итерации допустим pinned tag; digest предпочтительнее для полностью воспроизводимого состояния.
|
||
|
||
Проверка должна завершиться без создания LXC. При конфликте CTID/IP выбрать следующие свободные значения и использовать их согласованно во всех файлах.
|
||
|
||
## 2. Inventory
|
||
|
||
Изменить `ansible/inventory/hosts.yml`:
|
||
|
||
- добавить `grimmory` в `lxc_infra`;
|
||
- указать `ansible_host: 192.168.1.34`;
|
||
- указать `expected_lan_ip`;
|
||
- указать `ansible_user: root`;
|
||
- использовать существующий ProxyJump через `ru-vps`;
|
||
- добавить в `monitoring_exporters`, если Node Exporter разворачивается сразу.
|
||
|
||
После изменения проверить:
|
||
|
||
```bash
|
||
ansible-inventory --graph
|
||
ansible-inventory --host grimmory
|
||
```
|
||
|
||
Edge cases:
|
||
|
||
- не использовать IP за пределами маршрутизируемого service range `.5-.40`;
|
||
- убедиться, что имя `grimmory` разрешается Ansible именно в новый IP;
|
||
- не добавлять host в inventory до выбора окончательных CTID/IP во всех playbooks.
|
||
|
||
## 3. Provisioning LXC
|
||
|
||
Создать `ansible/playbooks/pve-grimmory.yml`, используя роль `pve_lxc`.
|
||
|
||
Параметры:
|
||
|
||
```yaml
|
||
pve_lxc_vmid: 149
|
||
pve_lxc_node: cloud-pc
|
||
pve_lxc_hostname: grimmory
|
||
pve_lxc_ip: 192.168.1.34/24
|
||
pve_lxc_gateway: 192.168.1.1
|
||
pve_lxc_storage: data
|
||
pve_lxc_disk: 64
|
||
pve_lxc_cores: 2
|
||
pve_lxc_memory: 4096
|
||
pve_lxc_swap: 1024
|
||
pve_lxc_unprivileged: true
|
||
pve_lxc_features:
|
||
- nesting=1
|
||
- keyctl=1
|
||
```
|
||
|
||
Дополнительно настроить Docker-in-LXC по существующему паттерну:
|
||
|
||
- разрешить `/dev/fuse`;
|
||
- добавить bind устройства `/dev/fuse`;
|
||
- перезапустить LXC только при изменении его PVE-конфигурации;
|
||
- дождаться SSH;
|
||
- проверить наличие пользовательского SSH-ключа.
|
||
|
||
Текущая роль `pve_lxc` не управляет `/dev/fuse`, поэтому эти задачи остаются в playbook, аналогично существующим Docker LXC.
|
||
|
||
Проверки:
|
||
|
||
```bash
|
||
ansible-playbook --syntax-check playbooks/pve-grimmory.yml
|
||
ansible-playbook playbooks/pve-grimmory.yml --check
|
||
```
|
||
|
||
`--check` для Proxmox API не гарантирует полноценной симуляции, поэтому его результат использовать только как дополнительную проверку.
|
||
|
||
После создания проверить:
|
||
|
||
- `pct status 149`;
|
||
- unprivileged mode включен;
|
||
- rootfs действительно находится на `data`;
|
||
- `onboot=1`;
|
||
- SSH работает через ProxyJump;
|
||
- `/dev/fuse` существует внутри LXC;
|
||
- повторный запуск playbook не создает изменений.
|
||
|
||
## 4. Docker Runtime
|
||
|
||
Внутри LXC установить:
|
||
|
||
- `docker.io`;
|
||
- Docker Compose v2, если доступен как `docker-compose-plugin`;
|
||
- `fuse-overlayfs`;
|
||
- `ca-certificates`;
|
||
- `curl`;
|
||
- `mariadb-client` для backup/restore-проверок;
|
||
- `prometheus-node-exporter`.
|
||
|
||
Настроить `/etc/docker/daemon.json`:
|
||
|
||
```json
|
||
{
|
||
"storage-driver": "fuse-overlayfs"
|
||
}
|
||
```
|
||
|
||
Проверить:
|
||
|
||
```bash
|
||
docker info
|
||
docker compose version
|
||
docker run --rm hello-world
|
||
```
|
||
|
||
Критерий успеха: Docker сообщает `fuse-overlayfs` как storage driver и переживает перезапуск LXC.
|
||
|
||
## 5. Persistent Data
|
||
|
||
Создать структуру:
|
||
|
||
```text
|
||
/opt/grimmory/
|
||
├── compose.yml
|
||
├── .env
|
||
├── data/
|
||
├── books/
|
||
├── bookdrop/
|
||
├── mariadb/
|
||
└── backup-staging/
|
||
```
|
||
|
||
Назначение:
|
||
|
||
- `data` -> `/app/data`;
|
||
- `books` -> `/books`;
|
||
- `bookdrop` -> `/bookdrop`;
|
||
- `mariadb` -> `/config`;
|
||
- `backup-staging` -> временные согласованные дампы.
|
||
|
||
Права каталогов должны соответствовать `APP_USER_ID`, `APP_GROUP_ID`, MariaDB `PUID/PGID`.
|
||
|
||
Edge cases:
|
||
|
||
- `bookdrop` не является библиотекой: после успешного импорта исходники удаляются оттуда;
|
||
- не размещать важные необработанные оригиналы только в `bookdrop`;
|
||
- не вкладывать bind mounts друг в друга;
|
||
- не использовать `DISK_TYPE=NETWORK`, поскольку файлы находятся на локальном rootfs;
|
||
- оставлять минимум 15-20% диска свободным: импорт и обработка могут временно удваивать размер книги;
|
||
- учитывать крупные PDF/CBR и возможные heap dump при OOM.
|
||
|
||
## 6. Compose
|
||
|
||
Управлять двумя контейнерами через Compose, поскольку Grimmory зависит от MariaDB. Для этого сервиса Compose логичнее отдельных systemd `docker run`.
|
||
|
||
Основные параметры:
|
||
|
||
- `DISK_TYPE=LOCAL`;
|
||
- `TZ` соответствует HomeLab;
|
||
- `ALLOWED_ORIGINS=https://books.ada-dev.ru`;
|
||
- MariaDB доступна только во внутренней Docker network;
|
||
- наружу публикуется только Grimmory `6060`;
|
||
- порт привязывается к LAN IP LXC, а не `0.0.0.0`, если Compose это позволяет;
|
||
- healthcheck использует `/api/v1/healthcheck`;
|
||
- `restart: unless-stopped`.
|
||
|
||
Создать systemd unit-обертку для:
|
||
|
||
```bash
|
||
docker compose -f /opt/grimmory/compose.yml up -d --remove-orphans
|
||
```
|
||
|
||
Не использовать `--pull always`: обновление должно быть отдельной контролируемой операцией.
|
||
|
||
Секреты:
|
||
|
||
- случайные отдельные пароли `DB_PASSWORD` и `MYSQL_ROOT_PASSWORD`;
|
||
- `.env` с mode `0600`;
|
||
- реальные значения не добавляются в Git;
|
||
- `.env.example` содержит только имена переменных и фиктивные значения;
|
||
- пароль первого администратора создается вручную через UI.
|
||
|
||
Проверки:
|
||
|
||
```bash
|
||
docker compose config
|
||
docker compose pull
|
||
systemctl start grimmory
|
||
docker compose ps
|
||
docker compose logs
|
||
curl -fsS http://127.0.0.1:6060/api/v1/healthcheck
|
||
```
|
||
|
||
Критерии:
|
||
|
||
- оба контейнера healthy;
|
||
- MariaDB не публикует порт на LAN;
|
||
- после рестарта LXC оба контейнера возвращаются в healthy;
|
||
- логи не содержат Flyway errors, authentication failures и циклических рестартов.
|
||
|
||
## 7. Первый запуск
|
||
|
||
1. Открыть сервис сначала по LAN.
|
||
2. Создать локального администратора.
|
||
3. Сохранить пароль в password manager.
|
||
4. Отключить неиспользуемую публичную регистрацию.
|
||
5. Создать отдельного обычного пользователя для чтения, если административная учетная запись не должна использоваться на BOOX.
|
||
6. Включить OPDS.
|
||
7. Создать отдельные OPDS credentials.
|
||
8. Выдать пользователю разрешение `Access OPDS`.
|
||
9. Создать тестовую библиотеку в `/books`.
|
||
10. Импортировать одну тестовую EPUB через BookDrop.
|
||
|
||
Важно: OPDS credentials отделены от обычного логина Grimmory. Пароль OPDS после создания нельзя получить повторно.
|
||
|
||
## 8. Reverse Proxy
|
||
|
||
Создать `ansible/playbooks/reverse-proxy-grimmory.yml` по паттерну Gitea/Vaultwarden.
|
||
|
||
Caddy site:
|
||
|
||
```caddy
|
||
books.ada-dev.ru {
|
||
reverse_proxy 192.168.1.34:6060
|
||
}
|
||
```
|
||
|
||
Caddy автоматически поддерживает WebSocket upgrade, но это нужно проверить для `/ws`.
|
||
|
||
Не добавлять дополнительный Caddy Basic Auth: он конфликтует с Basic Auth OPDS.
|
||
|
||
Проверки до переключения DNS:
|
||
|
||
- Caddy может достучаться до `192.168.1.34:6060` через OpenVPN;
|
||
- upstream healthcheck возвращает `2xx`;
|
||
- Caddyfile проходит `caddy validate`;
|
||
- конфигурация действительно смонтирована в контейнер Caddy.
|
||
|
||
После DNS:
|
||
|
||
```bash
|
||
curl -fsS https://books.ada-dev.ru/api/v1/healthcheck
|
||
```
|
||
|
||
Дополнительно проверить:
|
||
|
||
- валидную цепочку TLS;
|
||
- WebSocket `/ws`;
|
||
- правильные внешние `Host`, scheme и port;
|
||
- отсутствие redirect на внутренний `http://192.168.1.34:6060`;
|
||
- загрузку файла размером больше стандартного proxy limit;
|
||
- доступ с мобильной сети, а не только из домашнего LAN.
|
||
|
||
## 9. OPDS и BOOX Go 7
|
||
|
||
Каталог:
|
||
|
||
```text
|
||
https://books.ada-dev.ru/api/v1/opds
|
||
```
|
||
|
||
Настроить в NeoReader:
|
||
|
||
- URL корневого каталога;
|
||
- отдельный OPDS username/password;
|
||
- HTTPS;
|
||
- без второго слоя proxy-аутентификации.
|
||
|
||
Тестовый сценарий:
|
||
|
||
1. Каталог открывается.
|
||
2. Отображаются разделы и обложки.
|
||
3. Работает поиск.
|
||
4. EPUB скачивается.
|
||
5. Книга открывается в NeoReader.
|
||
6. Кириллица в названии, авторе и описании отображается корректно.
|
||
7. Повторная загрузка не создает поврежденный файл.
|
||
8. Проверить EPUB, PDF и FB2 отдельно.
|
||
9. Проверить работу из домашнего Wi-Fi и внешней сети.
|
||
10. Проверить поведение после смены OPDS-пароля.
|
||
|
||
Позиция чтения NeoReader обратно в Grimmory через OPDS не синхронизируется. Это ожидаемое поведение, а не дефект сервера.
|
||
|
||
## 10. Firewall
|
||
|
||
Минимальная политика:
|
||
|
||
- SSH `22` только из LAN и OpenVPN;
|
||
- `6060` только из LAN/OpenVPN и от нужного reverse-proxy маршрута;
|
||
- MariaDB не публикуется;
|
||
- остальные входящие соединения запрещены.
|
||
|
||
Edge case: Docker published ports могут обходить UFW. Поэтому проверить фактические nftables/iptables rules и доступ к `6060` с нежелательных сегментов, а не полагаться только на вывод `ufw status`.
|
||
|
||
Публично сервис должен быть доступен только через Caddy на `ru-vps`, не через router port-forward.
|
||
|
||
## 11. Monitoring
|
||
|
||
Добавить:
|
||
|
||
- Node Exporter target `192.168.1.34:9100`;
|
||
- LAN blackbox HTTP probe `/api/v1/healthcheck`;
|
||
- публичный HTTPS probe `https://books.ada-dev.ru/api/v1/healthcheck`;
|
||
- TLS expiry check;
|
||
- базовые алерты по availability, CPU, RAM, disk и backup freshness.
|
||
|
||
Не считать один health endpoint достаточным. Он может отвечать при частично неисправной функции OPDS.
|
||
|
||
Добавить синтетическую OPDS-проверку:
|
||
|
||
- Basic Auth тестового monitoring-пользователя;
|
||
- ответ `200`;
|
||
- ожидаемый OPDS content type;
|
||
- корректный XML/Atom;
|
||
- секреты только из runtime env, не из шаблона в Git.
|
||
|
||
Если хранить OPDS credential в monitoring нежелательно, ограничиться публичным healthcheck и ручным OPDS smoke test после обновлений.
|
||
|
||
## 12. PBS Backup
|
||
|
||
Добавить CTID в:
|
||
|
||
- `ansible/playbooks/pve-backup-jobs.yml`, job `homelab-pbs-daily-cloud`;
|
||
- `ansible/roles/backup_audit/defaults/main.yml`.
|
||
|
||
Сохранить исключение Docker merged layers:
|
||
|
||
```text
|
||
/var/lib/docker/fuse-overlayfs/*/merged
|
||
```
|
||
|
||
Проверки:
|
||
|
||
1. Применить backup job.
|
||
2. Запустить первый backup вручную.
|
||
3. Убедиться, что snapshot завершен без warnings.
|
||
4. Проверить наличие VMID `149` в PBS.
|
||
5. Проверить прохождение backup audit.
|
||
6. Проверить размер backup: слишком маленький может означать ошибочное исключение данных.
|
||
7. Выполнить тестовое file-level восстановление нескольких файлов.
|
||
8. Запланировать полный restore drill в отдельный временный CTID.
|
||
|
||
PBS snapshot MariaDB считается crash-consistent, но не заменяет логический dump.
|
||
|
||
## 13. Offsite Restic
|
||
|
||
Offsite должен содержать:
|
||
|
||
- согласованный `mariadb-dump`;
|
||
- `/opt/grimmory/data`;
|
||
- `compose.yml`;
|
||
- зашифрованную или отдельно защищенную конфигурацию;
|
||
- manifest с версиями Grimmory и MariaDB.
|
||
|
||
Не включать:
|
||
|
||
- `/opt/grimmory/books`;
|
||
- `/opt/grimmory/bookdrop`;
|
||
- `/opt/grimmory/mariadb` как raw-copy работающей БД;
|
||
- Docker layers;
|
||
- временные heap dump и cache.
|
||
|
||
Для дампа:
|
||
|
||
- использовать `mariadb-dump --single-transaction`;
|
||
- сначала писать во временный файл;
|
||
- проверять exit code;
|
||
- атомарно переносить завершенный dump в staging;
|
||
- только после этого запускать restic;
|
||
- удалять старый staging после успешного snapshot.
|
||
|
||
Расширить существующий restic-паттерн MariaDB-aware логикой, а не использовать SQLite backup helper.
|
||
|
||
Проверки:
|
||
|
||
- `mariadb` может прочитать dump;
|
||
- restic snapshot содержит dump и `/app/data`;
|
||
- backup audit отслеживает профиль Grimmory;
|
||
- выполнить пробное восстановление в отдельную временную MariaDB;
|
||
- убедиться, что восстановленная схема проходит Flyway startup на той же версии Grimmory.
|
||
|
||
## 14. Обновления
|
||
|
||
Обновлять только явно, например `v3.2.4 -> v3.2.5`.
|
||
|
||
Процесс:
|
||
|
||
1. Прочитать release notes и migration warnings.
|
||
2. Проверить свободное место.
|
||
3. Сделать MariaDB dump.
|
||
4. Сделать offsite restic snapshot.
|
||
5. Убедиться в наличии свежего PBS backup.
|
||
6. Изменить pinned image tag/digest.
|
||
7. Pull и restart.
|
||
8. Проверить Flyway logs.
|
||
9. Проверить health, UI, BookDrop и OPDS с BOOX.
|
||
10. Зафиксировать версию в документации.
|
||
|
||
Не выполнять code-only downgrade после применения Flyway migration. Для отката возвращать одновременно старый image и pre-upgrade backup базы.
|
||
|
||
## 15. Edge Cases
|
||
|
||
Обязательно проверить:
|
||
|
||
- заполнение rootfs во время массового импорта;
|
||
- дубликаты и одинаковые ISBN;
|
||
- книги без ISBN;
|
||
- кириллические имена и длинные пути;
|
||
- два файла одной книги в разных форматах;
|
||
- поврежденный EPUB/CBR;
|
||
- файлы больше 1 GB;
|
||
- cross-filesystem BookDrop move;
|
||
- перезапуск во время импорта;
|
||
- падение MariaDB во время обработки;
|
||
- недоступность Google Books/Open Library/Amazon;
|
||
- истечение TLS;
|
||
- смена внешнего домена;
|
||
- потеря OPDS-пароля;
|
||
- потеря admin-пароля;
|
||
- OOM и появление большого heap dump;
|
||
- несовместимая Flyway migration;
|
||
- неожиданное изменение floating Docker tag;
|
||
- частичный backup: БД без `/app/data` либо наоборот;
|
||
- восстановление БД с файлами библиотеки другого состояния.
|
||
|
||
## 16. Финальная приемка
|
||
|
||
Развертывание считается завершенным, когда:
|
||
|
||
- Ansible запускается повторно без изменений;
|
||
- LXC автоматически стартует;
|
||
- контейнеры healthy после reboot;
|
||
- MariaDB не доступна из LAN;
|
||
- HTTPS и WebSocket работают;
|
||
- NeoReader открывает OPDS и скачивает тестовые книги;
|
||
- импорт через BookDrop проходит полностью;
|
||
- Node Exporter и blackbox targets находятся в состоянии `UP`;
|
||
- создан и проверен PBS backup;
|
||
- создан и тестово восстановлен restic snapshot;
|
||
- секреты отсутствуют в Git diff;
|
||
- Obsidian-документация обновлена.
|
||
|
||
## 17. Ожидаемые файлы
|
||
|
||
Основные изменения:
|
||
|
||
- `ansible/inventory/hosts.yml`;
|
||
- `ansible/playbooks/pve-grimmory.yml`;
|
||
- `ansible/playbooks/reverse-proxy-grimmory.yml`;
|
||
- `ansible/playbooks/pve-backup-jobs.yml`;
|
||
- `ansible/roles/backup_audit/defaults/main.yml`;
|
||
- `ansible/playbooks/offsite-restic-yadisk.yml`;
|
||
- `ansible/playbooks/backup-audit.yml`;
|
||
- `ansible/roles/monitoring_server/templates/prometheus.yml.j2`;
|
||
- возможно `ansible/roles/monitoring_blackbox/templates/push-metrics.sh.j2`;
|
||
- `ansible/.env.example`, только если потребуются новые runtime secrets.
|
||
|
||
Документация:
|
||
|
||
- `Notes/Текущее состояние HomeLab после миграции на Proxmox.md`;
|
||
- `Log.md`;
|
||
- `Context.md`;
|
||
- при необходимости `HomeLab.md`.
|
||
|
||
## 18. Открытый параметр
|
||
|
||
Перед реализацией подтвердить `64 GB` как начальный размер диска или выбрать его по текущему объему коллекции.
|
||
|
||
Решение: текущая коллекция меньше `20 GB`, начальный rootfs оставлен `64 GB`.
|
||
|
||
## Журнал выполнения
|
||
|
||
### 2026-07-28 — preflight и проектные решения
|
||
|
||
- Прочитаны актуальные HomeLab notes и существующие Ansible-паттерны LXC, Caddy, monitoring, PBS и restic.
|
||
- CTID `149` отсутствует в `pct list` на `cloud-pc` и `mini-pc`, а также среди исторических snapshot в PBS.
|
||
- IP `192.168.1.33` отсутствует в inventory и AdGuard config, не отвечает на ping, ARP-состояние с `cloud-pc` — `INCOMPLETE`; пользователь подтвердил отсутствие DHCP reservation.
|
||
- Storage `cloud-pc:data` активен: доступно около `836 GB`, занято `3.67%`.
|
||
- PBS storage и datastore `pbs` активны; последние snapshots текущих cloud-pc CT созданы `2026-07-27`.
|
||
- `books.ada-dev.ru` уже разрешается в `157.22.231.198` (`ru-vps`); случайное имя в зоне не разрешается, поэтому это не wildcard. До добавления Caddy site TLS handshake не завершается.
|
||
- Подтверждены `grimmory/grimmory:v3.2.4` и `lscr.io/linuxserver/mariadb:11.4.8`; выбраны OCI index digests `sha256:dfa7afdfcf25d649fd664497a62385dd00cd9678c37546e182c172e41c8e80cb` и `sha256:91de7f701bc7fc3a424b81beafca7a7c6c4c5b7c8be6afd2ae148698695c0b0c`.
|
||
- Rootfs подтвержден как `data:64`, timezone — `Europe/Moscow`.
|
||
- Пароли MariaDB будут случайно созданы один раз внутри LXC и сохранены только в `/opt/grimmory/.env` с mode `0600`.
|
||
- В первой итерации автоматизируются health/TLS probes; OPDS smoke test остается ручным после создания credentials.
|
||
- Offsite audit первой итерации: атомарный MariaDB dump, проверка наличия dump в snapshot и `restic check`; автоматический restore в MariaDB отложен.
|
||
- Первый apply остановился до создания из-за quoting условия VMID guard; второй — из-за отсутствия прямого маршрута к Proxmox API. Для API использован временный SSH local-forward через `ru-vps`.
|
||
- После создания CT выяснился IP conflict: с `mini-pc` адрес `.33` резолвился как `YandexStationMini_1DD2.loc` и отклонял TCP/22, тогда как локальный bridge `cloud-pc` видел LXC. Выбран следующий адрес `192.168.1.34`; он не отвечает с обоих PVE-узлов, ARP — `INCOMPLETE`, DNS отсутствует.
|
||
|
||
### 2026-07-28 — реализация и автоматическая приёмка
|
||
|
||
- Создан unprivileged LXC `grimmory` CT `149` на `cloud-pc`: `192.168.1.34/24`, `data:64`, 2 vCPU, 4096 MB RAM, 1024 MB swap, `onboot=1`, `nesting=1`, `keyctl=1`, `/dev/fuse`.
|
||
- Из-за ограничений API token роль `pve_lxc` используется в create-only mode для Grimmory; VMID guard запрещает менять чужой CT, а network/keyctl/FUSE идемпотентно управляются через `pct` на PVE node.
|
||
- Установлены Docker Compose v2 `2.26.1`, Docker `26.1.5`, `fuse-overlayfs`, MariaDB client, Node Exporter и UFW. Docker подтвердил storage driver `fuse-overlayfs`.
|
||
- Случайные DB passwords созданы один раз внутри LXC; `/opt/grimmory/.env` имеет mode `0600`, значения не выводились и не добавлялись в Git.
|
||
- Compose запустил pinned Grimmory `v3.2.4` и MariaDB `11.4.8`; первый startup применил 142 Flyway migrations и занял около 6 минут. Health wait увеличен до 10 минут.
|
||
- После reboot LXC оба контейнера вернулись в `healthy`; MariaDB не слушает host/LAN port, Grimmory привязан к `192.168.1.34:6060`.
|
||
- UFW ограничивает SSH/`6060` LAN и OpenVPN, `9100` разрешён только от monitoring LXC. Отдельная `GRIMMORY-FILTER` chain в `DOCKER-USER` закрывает обход UFW через Docker published port.
|
||
- Caddy site применён. Проверены public health `200`, сертификат Let's Encrypt до `2026-10-26` и WebSocket/STOMP Upgrade `101` на `/ws`.
|
||
- Prometheus targets: Node Exporter `up=1`, LAN health `probe_success=1`, public Grimmory probe `probe_success=1`; TLS expiry покрыт существующим alert.
|
||
- CT `149` добавлен в `homelab-pbs-daily-cloud` и PBS audit. Первый PBS backup `ct/149/2026-07-28T14:47:32Z` успешен, размер `2583749785` bytes; audit success, file-level restore трёх объектов проверен.
|
||
- Ограничение PBS: storage `data` не поддерживает native LXC snapshots, поэтому job с mode `snapshot` автоматически перешёл в `suspend`; фактическая пауза гостя составила около 1 секунды.
|
||
- Создан restic snapshot `a3b012ab` в Yandex Disk. Включены atomic MariaDB dump, `/opt/grimmory/data`, Compose, manifest и зашифрованная restore-конфигурация; books/BookDrop/raw MariaDB исключены. `restic check` и restore непустого `grimmory.sql` прошли.
|
||
- Повторные runs `pve-grimmory.yml`, `reverse-proxy-grimmory.yml`, `pve-backup-jobs.yml`, Grimmory offsite/audit и limited monitoring завершились с `changed=0`.
|
||
- Обновлены Obsidian current state, `Log.md` и `Context.md`.
|
||
- Reverse DNS для `.34` содержит stale имя телефона, но ARP на обоих PVE nodes показывает MAC LXC `bc:24:11:43:75:31`. Нужно исключить `.34` из DHCP pool или закрепить reservation, чтобы предотвратить будущий конфликт.
|
||
|
||
### Требует участия пользователя
|
||
|
||
- Создать первого локального администратора и сохранить пароль в password manager.
|
||
- Отключить public registration, создать обычного reader-пользователя.
|
||
- Создать библиотеку `/books`, выполнить тестовый BookDrop import.
|
||
- Включить OPDS, создать отдельные OPDS credentials и выдать `Access OPDS`.
|
||
- Проверить EPUB, PDF и FB2 на BOOX Go 7 через Wi-Fi и внешнюю сеть.
|
||
- После создания OPDS пользователя решить, добавлять ли credential в synthetic monitoring; сейчас OPDS smoke test ручной.
|
||
- Создать DHCP reservation/exclusion для `192.168.1.34`.
|
||
- Полный restore drill CT и восстановление dump во временную MariaDB остаются отдельной плановой операцией; file-level restore и dump restore-from-restic уже проверены.
|
||
|
||
### 2026-07-28 — Caddy KOReader и OPDS compatibility
|
||
|
||
- `reverse-proxy-grimmory.yml` разделяет device endpoints `/api/koreader/**` и `/api/v1/opds/**` в отдельный Caddy handler.
|
||
- Для этих endpoints upstream compression отключён и установлен `Accept-Encoding: identity`; Caddy не добавляет proxy Basic Auth и не изменяет `Authorization`, `X-Auth-*`, `Range`, `WWW-Authenticate` или download headers.
|
||
- Запросы с `Accept-Encoding: gzip, br` подтверждённо возвращают `401` без `Content-Encoding` для KOReader и OPDS. Caddy validate, Grimmory health и idempotent rerun успешны.
|
||
- Дополнительно исправлен Grimmory OPDS `500` из-за `No acceptable representation`: Caddy нормализует `Accept` только для Atom/OpenSearch catalog routes, оставляя original `Accept` для book download/cover routes.
|