Files
infra/servers/rpi.md
T

273 lines
92 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# rpi — домашняя Raspberry Pi 4 (`rpi.local`, 192.168.10.25)
Поднята 2026-10-11. Домашний сервер за NAT: первая задача — бэкап личного
OneDrive на внешний HDD; с того же дня ещё и медиацентр (Jellyfin + *arr, Docker). Мониторится с fr1 через pushgateway (скрейпить
её снаружи нельзя — она за домашним роутером).
## Железо и ОС
- Raspberry Pi 4, SD 64 ГБ (`mmcblk0`), питание штатное — `vcgencmd get_throttled` = `0x0`.
- Raspberry Pi OS Lite 64-bit **Trixie** (Debian 13), образ `2026-10-06-raspios-trixie-arm64-lite`.
- Headless-настройка — **cloud-init**, а не старые `ssh`/`userconf.txt`: в Trixie Pi Imager пишет `user-data`/`network-config` на `bootfs`. Наш `user-data` создал пользователя, ключ, hostname, таймзону MSK.
- Сеть: Ethernet 1 Гбит/с, DHCP. MAC `dc:a6:32:db:b0:f9`, роутер `192.168.10.254` (Huawei HG8245X6-10, Cyta). Адрес `192.168.10.25` закреплён за MAC статической DHCP-привязкой на роутере (2026-10-11; была без резерва).
## Доступ
- С Mac: `ssh rpi` (алиас → `rpi.local`, user `rus`, ключ `~/.ssh/id_ed25519`). Резолвится через mDNS (avahi в образе по умолчанию).
- Только по ключу: `passwordauthentication no`, у `rus` пароль заблокирован, **sudo NOPASSWD** (иначе sudo был бы недоступен вовсе).
## Диски
| Устройство | Модель | Роль |
|---|---|---|
| `sda` | WD My Passport 4 ТБ (WD40NDZW, s/n WD-WX82D6228YCD) | ext4 `backup` → `/mnt/backup` |
| — | WD Elements 1 ТБ (WD10SDRW) | **в USB роутера**, не малины: NTFS/MBR, метка `ROUTER` → `router:` / `/mnt/router` (см. «Вторая копия») |
- fstab по UUID с `nofail,x-systemd.device-timeout=30s` (`stacks/rpi-backup/fstab.line`): без диска малина грузится, бэкап просто пропускается (`mountpoint -q`).
- ⚠️ **Два 2.5" HDD от USB Pi 4 не тянут.** Проверено 2026-10-11: при раскрутке обоих — `over-current` на всех USB3-портах; под нагрузкой 4-ТБ диск отваливался (`USB disconnect`) и залипал в `Unit Not Ready`. Один диск стабилен (20 ГБ чтения 103 МБ/с, mkfs ~60 ГБ записи без сбросов). Второй диск — только через USB3-хаб с собственным питанием.
- Залипший после отвала диск лечится power-cycle всех USB (Pi 4 переключает питание портов только разом): `sudo uhubctl -l 2 -a off; sudo uhubctl -l 1-1 -a off; sleep 8; …-a on`.
- SMART на момент ввода: 9 897 ч, 0 переназначенных/ожидающих, Start_Stop 21 946, Load_Cycle 42 530.
## Бэкап OneDrive (код — `stacks/rpi-backup/`)
- rclone (официальный .deb, v1.75.2 — в Debian 1.60, старый), remote `onedrive:` = личный OneDrive (drive_type personal). Токен — в `~rus/.config/rclone/rclone.conf` (600), только на малине. Получен через `rclone authorize onedrive` на Mac.
- `onedrive-backup` (таймер каждые 4 ч, `00/4:30`): `rclone sync` в `/mnt/backup/onedrive/current`, всё удалённое/перезаписанное в облаке — в `archive/<ts>/` **навсегда** (пользователь хочет иметь всё, что когда-либо было в OneDrive). Хэши архива — `manifests/archive-<ts>.qxh`, лог каждого прогона — `logs/sync-<ts>.log` (INFO: поштучно что скачано/убрано в архив).
- Целостность: rclone сверяет QuickXorHash каждого скачанного файла с OneDrive; `onedrive-verify` (вс 05:00) перечитывает весь HDD: `rclone check` current↔облако + `rclone checksum` archive↔манифесты. Отчёт — `logs/verify-<ts>*`.
- Оба юнита под общим `flock /run/lock/onedrive-backup.lock` — не идут параллельно и не убивают друг друга.
- ⚠️ Файл, загруженный и удалённый в облаке между прогонами (<4 ч), в бэкап не попадёт.
- ⚠️ `Personal Vault` исключён — rclone через API туда не ходит.
- ⚠️ Архив без ротации: растёт вечно. Скрипт выходит с кодом 2 при заполнении >90 % (→ алерт BackupFailed), плюс HostDisk* если когда-то поставим node-exporter.
- Первая полная выгрузка (368 ГБ) запущена 2026-10-11 ~13:13, ~27 МБ/с (упор в домашний канал).
## Вторая копия — 1 ТБ диск в роутере (с 2026-10-11)
- Диск воткнут в роутер Huawei (`192.168.10.254`), а не в малину — **не ест питание USB малины** (два HDD от неё не тянут, см. «Диски»). Доступ — SFTP роутера на порту **8022**, пользователь `root`, remote `router:` в `~rus/.config/rclone/rclone.conf` (пароль только там), host key — `~rus/.ssh/known_hosts_router`.
- ⚠️ Роутер не считает хэши (`shell_type = none`, `md5sum_command = none`) — rclone сравнивает по размеру+mtime, целостность проверяется перечиткой.
- `mnt-router.service` — `rclone mount router: /mnt/router` (user rus, `--vfs-cache-mode writes`, кэш в `/mnt/backup/.rclone-cache`). Для просмотра/ручной работы; бэкап пишет **напрямую в remote**, не через FUSE (коды ошибок, без кэша).
- Скорость (замер 2026-10-11, параллельно с выгрузкой OneDrive): запись ~8 МБ/с, чтение ~11 МБ/с — первичная копия ~370 ГБ ≈ 13 ч. Диск 932 ГБ.
- `router-backup` (`stacks/rpi-backup/`, таймер 06:00, `flock`): в `router:rpi-backup/` — `onedrive/{archive,manifests,logs}` только `copy` (там ничего не удаляется), затем зеркало `onedrive/current` с `--max-delete 1000`, затем зеркало зашифрованных `servers/` с `--max-delete 50`. Порядок важен: удалённое из current к этому моменту уже скопировано в archive. Логи — `/mnt/backup/router-logs/`.
- ⚠️ `--max-delete` — предохранитель: если локально «пропадёт» много файлов (сбой диска/монтирования), копия на роутере не зачистится, а прогон упадёт с ошибкой → алерт `BackupFailed`.
- По воскресеньям `verify`: сверка размеров деревьев + перечитать с роутера последний daily каждого сервера и сравнить sha256.
- **Ежемесячно (1-го, 08:00) `router-sample`**: случайные файлы OneDrive из `current/` и `archive/` старше суток, ~2 ГБ / до 300 шт., перечитываются с роутера и сверяются sha256 с HDD — ловит тихую порчу, которую сверка размеров не видит. Отсутствующий на роутере файл = ошибка. Итог — `backup_*{job="backup_router_sample"}` → `BackupFailed`, `BackupStale` (>35 дней). Проверено 2026-10-11: 20 из 20 скопированных файлов совпали.
- Мониторинг: `router_mount_up` и заполнение `/mnt/router` (в `smart-metrics`, `df` под `timeout 20`), итог прогона — `backup_*{job="backup_router"}`; алерты `RouterDiskMountDown` (30 мин), `BackupDiskFull` (>90 %, любой диск бэкапа), `BackupStale` (>30 ч); ряд «Вторая копия» на дашборде rpi-disk.
- ⚠️ 1 ТБ меньше основного диска: сейчас влезает (~370 ГБ OneDrive + ~100 ГБ серверов), но архив версий растёт вечно — следить за `BackupDiskFull`.
- Почему NTFS (2026-10-11): прошивка Cyta видит FAT32 и NTFS, **exFAT — нет** (диск в списке USB не появлялся); FAT32 отпал из-за лимита 4 ГБ на файл. Файл 4,4 ГБ через SFTP записан и прочитан. На Mac NTFS штатно не создать — делали `mkntfs` из ntfs-3g, собранного без FUSE (`--disable-ntfs-3g`), на блочное `/dev/diskNs1` (на `rdisk` mkntfs отказывается).
- ⚠️ FTP роутера (порт 21) включён в его конфиге, но из LAN **filtered**: доступ к FTP со стороны LAN открывается только на странице ONT Access Control, а у пользователя `cytauser` её нет (нужен суперадмин Cyta). Поэтому SFTP: включён 2026-10-11 через веб-API роутера — сервер `8022`, LAN=1, **WAN=0**, аккаунт `root` с корнем `/mnt/usb/usb-1058-363434_1`. Пароли SFTP и веб-интерфейса — только у пользователя и в `rclone.conf`.
- ⚠️ Потолок ~10 МБ/с — CPU роутера (dropbear): `aes128/256-ctr` вдвое медленнее `chacha20` (нет аппаратного AES), параллельные потоки почти не помогают. Быстрее только диск на малине через хаб с питанием (~100 МБ/с).
## Бэкап серверов fr1/ru1 (с 2026-10-11, код — `stacks/server-backup/`)
- Малина — внешняя площадка для VPS: `servers-backup.timer` (02:15) забирает по SSH выгрузки, которые **сервер сам шифрует открытым ключом age** (`tar | zstd | age`). Закрытого ключа на малине нет — ни она, ни Astra, ни взломщик архивы не прочитают. Ключ у владельца: Mac `~/.config/age/servers-backup.key`.
- Хранилище `/mnt/backup/servers/<host>/{daily,weekly,monthly}` (root 700): 14 / 8 / 12, monthly — хардлинк первого daily месяца. fr1 daily ~2.1 ГБ (5.5 мин), ru1 ~17 МБ, fr1 weekly (вс) — тома ботов и imgbot.
- Ключ `/etc/server-backup/id_ed25519` (root 600): на серверах ограничен forced command — только запустить выгрузку. Host keys закреплены в `/etc/server-backup/known_hosts`.
- Целостность: sha256 шифротекста считает сервер, малина сверяет при приёме и по воскресеньям (`servers-backup verify`). Итог — метрики `backup_*{job="backup_servers"}`, алерт `BackupStale` >26 ч, панель на дашборде rpi-disk.
- Пробное восстановление 2026-10-11 на Mac: оба архива расшифрованы, на месте CA OpenVPN (`easy-rsa/pki/private/ca.key`), ключи AWG, конфиги прокси, репо gitea, снимок `gitea.db`.
## Мониторинг → fr1
- `smartd` (`stacks/rpi-backup/smartd.conf`): `DEVICESCAN -d removable`, `-n standby,q` (не будить диск), короткий самотест сб 03:00, длинный 1-го числа 05:00. ⚠️ `-d sat` с DEVICESCAN не работает («Not a SAT device») — тип моста пусть определяет сам.
- `smart-metrics` (таймер 15 мин): smartctl `-n standby` → метрики `smart_*` → job `smart`. Спящий диск не будится: отдаются значения из кэша `/var/lib/smart-metrics/` + `smart_disk_standby=1`.
- `backup-metrics <job>` — `ExecStopPost=+` у юнитов бэкапа: `backup_last_exit_code`, `backup_last_run_timestamp`, `backup_last_success_timestamp` (время успеха хранится локально в `/var/lib/backup-metrics/`, т.к. PUT заменяет группу целиком).
- Доставка: `metrics-push <job>` = ssh с ключом `/etc/metrics-push/id_ed25519` (root, 600) на `rus@161.97.93.252`; host key fr1 закреплён в `/etc/metrics-push/known_hosts`. На fr1 ключ ограничен forced command (`servers/fr1.md` → Мониторинг).
- `smart-metrics` заодно отдаёт `backup_disk_size_bytes`/`backup_disk_avail_bytes` для `/mnt/backup` (statfs, диск не будит).
- Дашборд: https://mon.ruzzy.dev/d/rpi-disk (SMART, износ за сутки, свежесть бэкапов, заполнение).
- ⚠️ `BackupStale` видит только **существующие** серии — задача, ни разу не отчитавшаяся, им не ловится. Для этого `BackupNeverReported` на `absent()` (с 2026-10-11): OneDrive 12 ч, серверы 26 ч, роутер 30 ч, проверка OneDrive 8 дней; и `PushMetricsStale` на отсутствие SMART-метрик 1 ч. Доставка в Telegram проверена тестовым алертом 2026-10-11.
- **monwatch — сторож самого мониторинга** (с 2026-10-11, `stacks/rpi-backup/bin/monwatch`, таймер раз в час): весь мониторинг живёт на fr1, и если упадёт fr1 или цепочка доставки — алертов не будет вообще. Малина спрашивает fr1 `health` (тот же ключ метрик, режим только чтение): живы ли Prometheus/Alertmanager/pushgateway, видит ли Prometheus пуши малины моложе часа, не растут ли `alertmanager_notifications_failed_total{integration="telegram"}`. При проблеме (после повторной проверки через 2 мин) пишет в Telegram **напрямую, минуя fr1** — ботом @rasp_contol_bot (токен из `/etc/astra-bot.env`) в чат 607015; повтор раз в 6 ч, ✅ при восстановлении. Состояние — `/var/lib/monwatch/`. Путь тревоги проверен имитацией 2026-10-11 (`MONWATCH_MAX_PUSH_AGE=1`).
- ⚠️ Сообщения сторожа приходят в чат с ботом Astra — это бот сам пишет, Codex их не видит и не обрабатывает.
- Алерты — группа `rpi` в `stacks/monitoring/prometheus/rules/alerts.yml`.
## Домашняя печать (с 2026-10-11)
- Brother DCP-L2620DW обнаружен через mDNS: `192.168.10.24`, `BRW2C9C58C1EEA7.local`, IPP порт 631 `/ipp/print`; чёрно-белый, A4, двусторонняя печать. Ранее службы печати на rpi не было.
- Установлены `cups`, `cups-client`, `cups-filters`, `cups-ipp-utils` (CUPS 2.4.10); включён `cups.service`. Очередь по умолчанию `home-brother`, драйвер `everywhere`, URI `ipp://BRW2C9C58C1EEA7.local:631/ipp/print`. По умолчанию A4, одна сторона; `printer-is-shared=false`, CUPS слушает только `127.0.0.1:631` и `[::1]:631`. Доступ из Telegram — команда агента, без отдельного открытого веб-интерфейса.
- Код настройки и команда — `stacks/rpi-print/`; `home-print` установлен в `/usr/local/bin/` (root, 755). `home-print status` — состояние/очередь; `home-print [--duplex] <PDF/PNG/JPEG>` — одна копия, A4 с подгонкой. Печать только по прямой просьбе пользователя; приём задания в очередь не означает, что бумага уже вышла.
- **Навык `home-print` упрощён для ребёнка 2026-10-11**; источник — [astra-bot `11d0a85`](https://git.ruzzy.dev/ruzzy/astra-bot/commit/11d0a85), `codex/skills/home-print/`, установлен в `~astra/.codex/skills/home-print/`. Вместо PNG каждой страницы и повторного подтверждения отправляет **один PDF**, затем по прямой просьбе «распечатай» печатает одну копию A4 с одной стороны без дополнительных вопросов. Текст извлекается из скриншотов, картинки сохраняются в оттенках серого, все страницы проверяются локально. Если PDF не доставлен, печать не запускается; повторная отправка задания при ошибке не автоматическая. Для этого сценария установлены узкие постоянные разрешения `~astra/.codex/rules/home-print.rules` (источник `codex/rules/` в astra-bot): только `/usr/local/bin/home-print` и PDF-режим штатного отправщика. Общая политика подтверждений сохранена. Проверка: 31 тест, валидация скилла и правил; произвольный Python правилами не разрешается. Инструкции установлены в `~/.codex/AGENTS.md`; новые разговоры получают их автоматически.
- **Кнопки и промежуточные ответы печати убраны в самом боте 2026-10-11**, [astra-bot `6b1317d`](https://git.ruzzy.dev/ruzzy/astra-bot/commit/6b1317d). Ранее постоянных правил скилла было недостаточно: запросы approval и все agentMessage уходили в Telegram. Бот теперь автоматически отвечает accept только на штатную PDF-отправку разрешённому чату и home-print для print.pdf в каталогах /tmp/astra-print-*; shell-операторы и произвольные команды исключены. В задачах печати сохраняет последний ответ и отправляет его после завершения, PDF доставляется отдельным вложением. Проверено 34 тестами, установлен bot.py; прежний код — /opt/astra-bot/bot.py.bak-20261011-child-print. Перезапуск через restart-check.py после завершения текущего ответа.
- ⚠️ DOCX напрямую не печатается — сначала преобразовать в PDF. Hostname выбран вместо DHCP-IP, чтобы пережить смену адреса; требуется mDNS и включённый принтер. `printer-error-policy=abort-job` исключает неожиданную автоматическую печать старых заданий после ошибки; ошибочный прогон нужно отправить заново.
- Проверено: запрос атрибутов IPP (`ipptool`) PASS, принтер idle/enabled, A4/Gray/duplex доступны. Портрет успешно преобразован фильтрами CUPS PNG → PDF → PWG Raster (A4, 300 dpi), без ошибок. Тестовая страница A4 отправлена 2026-10-11 в 15:20 MSK: `home-brother-1`, CUPS сообщил `job-completed-successfully`, принтер вернулся в idle, очередь пуста; качество на бумаге проверяет пользователь.
- Для перевёрстки извлечённого текста в PDF установлен `python3-reportlab` 4.3.1 (2026-10-11; ранее средств создания PDF с кириллицей не было), с зависимостями Pillow/Cairo. Шрифт DejaVu Sans внедряется в PDF; PNG-предпросмотр строится из итогового серого PDF. Проверено на комплекте Лермонтова: 3 страницы A4, текст со скриншотов и исходный портрет.
## Медиацентр (с 2026-10-11, код — `stacks/rpi-media/`)
Docker CE 29.9 + Compose v5.6 из репо download.docker.com (в Debian старее). `/etc/docker/daemon.json`: json-file `10m×3` — чтобы логи контейнеров не точили SD. `rus` в группе `docker` (= root-эквивалент; у него и так sudo NOPASSWD). Стек — `/opt/media/docker-compose.yml`, конфиги — `/opt/media/config/<сервис>` (SD, ~20 МБ), образы ~7.8 ГБ на SD.
| Сервис | Порт | Зачем | Версия на 2026-10-11 |
|---|---|---|---|
| Jellyfin | 8096 (host-сеть) | медиасервер; клиенты LG webOS, Infuse/Swiftfin на iOS | 12.2 |
| Seerr | 5055 | каталог «хочу посмотреть» → Radarr/Sonarr | v3.5.0 |
| qBittorrent | 8080, торренты 6881 tcp/udp | загрузка | 5.2.4 |
| Radarr / Sonarr | 7878 / 8989 | фильмы / сериалы: поиск, загрузка, импорт | 6.4.4 / 4.0.20 |
| Prowlarr | 9696 | индексаторы, раздаёт их в Radarr/Sonarr | 2.6.5 |
| FlareSolverr | 8191 | обход Cloudflare для индексаторов с тегом `flaresolverr` | v3.5.2 |
| Bazarr | 6767 | субтитры, профиль `ru+en` | v1.6.2 |
| Homepage | 3000 | **стартовая страница**: все сервисы с описанием по-русски, живой статус, CPU/температура/диск | v2.5.0 |
- **Данные — `/mnt/backup/media`** (4-ТБ диск, решение пользователя): `torrents/{movies,tv,other}`, `library/{movies,tv}`, `.cache/jellyfin` (кэш и транскоды Jellyfin — не на SD). qBittorrent и *arr монтируют **один и тот же** корень как `/data` — иначе импорт из `torrents/` в `library/` превращается из хардлинка в копию и раздача занимает место дважды. `copyUsingHardlinks` включён. В бэкапы (`router-backup`, `onedrive-*`) `media/` не входит.
- ⚠️ **Медиа делят диск с бэкапами.** `onedrive-backup` падает с кодом 2 при заполнении >90 % (~370 ГБ свободно) → `BackupFailed`. В Radarr/Sonarr стоит `minimumFreeSpaceWhenImporting = 500 ГБ`: ниже этого импорт останавливается. **qBittorrent своего порога не имеет** — ручная заливка торрентов мимо *arr может забить диск.
- Jellyfin в `network_mode: host` — иначе LG/iOS не находят сервер автообнаружением (UDP 7359 и DLNA-мультикаст не проходят через бридж). Аппаратного транскодинга на Pi 4 нет (V4L2 убран в Jellyfin 10.9), `EncodingThreadCount=3`: расчёт на Direct Play. ⚠️ Транскод 4K/HEVC Pi не тянет — клиенту нужен нативный кодек (Infuse играет почти всё сам).
- Метаданные и интерфейс — ru, предпочтительные аудио/субтитры у пользователя — `rus`, режим субтитров Smart. Библиотеки «Фильмы» (`/data/library/movies`) и «Сериалы» (`/data/library/tv`), в Jellyfin только чтение.
- Индексаторы (публичные, без аккаунтов): NoNaMe Club, The Pirate Bay, YTS — синхронизированы в Radarr/Sonarr; Rutor и EZTV есть в Prowlarr, но в *arr не доехали (Rutor не проходит тест по категориям, EZTV на момент настройки лежал); 1337x не добавился (таймаут даже через FlareSolverr). **RuTracker** (добавлен 2026-10-11, аккаунт пользователя; логин/пароль — только в БД Prowlarr): через FlareSolverr, опции «убрать кириллицу», «добавить RUS», «первые теги в конец» — чтобы *arr сопоставляли английские названия и видели русскую дорожку; приоритет 10 (выше публичных, 25). Синхронизирован в Radarr и Sonarr. Kinozal и прочие приватные — руками.
- ⚠️ RuTracker закрывает `login.php`/`tracker.php` челленджем Cloudflare (по IPv4; IPv6 у малины нет маршрута). FlareSolverr на Pi решает его **80–180 с** — у прокси в Prowlarr `requestTimeout=180` (было 60, с ним добавление падало по таймауту). После решения Prowlarr держит cookie и поиск идёт ~4 с; когда cookie протухнет, первый поиск снова будет минутами, и *arr могут на время пометить индексатор недоступным.
- **Языки дорожек** (2026-10-11, `provision.py langs`): custom formats «Русская дорожка» (язык Russian или `RUS|MVO|DVO|Dub|Дубляж` в названии) = 100 и «Оригинальная»/«Английская дорожка» = 50 во всех профилях Radarr/Sonarr; `minFormatScore=0` (ничего не отклоняется), `cutoffFormatScore=150` + `upgradeAllowed` — *arr сами перекачают, когда появится раздача с русской дорожкой. Язык профилей Radarr — Any (было Original: раздачи только с дубляжом отклонялись). Повод — первый фильм (The Fix, 2026) скачался с YTS только на английском; русского дубляжа у свежих релизов ещё нет неделями, есть только `Sub`-раздачи.
- Bazarr: провайдеры без аккаунтов (`embeddedsubtitles`, `podnapisi`, `yifysubtitles`); для русских субтитров лучший — OpenSubtitles.com, нужен аккаунт. ⚠️ Бесплатные провайдеры для свежих фильмов русских субтитров не находят (The Fix — пусто); тогда субтитры вытаскивали из `Sub`-раздачи RuTor: mkv в `torrents/other`, `ffmpeg -map 0:s:m:language:rus -c copy` → `<имя>.ru.srt` рядом с фильмом (сверить длительность/fps — иначе рассинхрон), лишнюю раздачу удалить.
- **Авторизация:** везде пользователь `rus`, один пароль — `/opt/media/.admin-password` (rus 600). *arr — формы, пароль **всегда** (режим «без пароля из LAN» в новых *arr требует `AllowedHosts`). Между собой сервисы ходят по API-ключам из `config/<app>/config.xml`.
- **Пароли сняты для домашней сети 2026-10-11** (решение владельца): Radarr/Sonarr/Prowlarr — `AuthenticationRequired=DisabledForLocalAddresses` (из LAN/частных сетей без входа, снаружи пароль бы остался; API по-прежнему по ключу — UI берёт его из `/initialize.json`); qBittorrent — `bypass_auth_subnet_whitelist` 192.168.10.0/24 + 172.16.0.0/12 и `bypass_local_auth` (учётка `rus` для *arr не тронута); Bazarr — `auth.type: null`. Бэкапы конфигов — `config/<app>/*.bak-20261011-auth`. Jellyfin: пустой пароль у админа запрещён самим Jellyfin 12 («Admin user passwords must not be empty») — поэтому заведён пользователь **«Дом»** без пароля и без прав админа (все библиотеки, без удаления), он один на экране входа; `rus` (админ) — с паролем, для настроек. Seerr по-прежнему входит через учётку Jellyfin. ⚠️ Теперь любое устройство в домашней сети (гости в Wi‑Fi, умный дом) управляет загрузками и библиотекой. ⚠️ `provision.py` на чистой установке снова включит пароли — при переустановке повторить.
- **Точка входа для пользователя — http://192.168.10.25:3000** (Homepage, конфиги `stacks/rpi-media/homepage/` → `/opt/media/homepage/`). Ключи виджетов — `/opt/media/homepage.env` (rus 600, `HOMEPAGE_VAR_*`, генерит `provision.py homepage`). ⚠️ Там же пароль qBittorrent: после смены общего пароля перегенерировать env и `docker compose up -d homepage`, иначе виджет qBittorrent отвалится. `HOMEPAGE_ALLOWED_HOSTS` обязателен — без него 400 на любой Host, кроме localhost.
- ⚠️ Встроенный виджет `jellyfin` Homepage 2.5 с Jellyfin 12 не работает: ходит на `/emby/…?api_key=` — пути нет (404), ключ в query отключён (`EnableLegacyAuthorization=false`, включать не стали). Вместо него `customapi` на `/Items/Counts` с заголовком `Authorization: MediaBrowser Token=…`.
- Radarr/Sonarr → Jellyfin (Connect «Jellyfin», тип MediaBrowser, ключ Jellyfin `homepage`; `provision.py connect` на чистой установке заводит ключ `arr-connect`): библиотека обновляется сразу после импорта. Без этого новый фильм висел невидимым до скана по расписанию — realtime-монитор хардлинк не заметил (проверено 2026-10-11 на первом фильме).
- Первичная связка — `stacks/rpi-media/provision.py` (идемпотентный, пароль и ключи читает на месте): `python3 /opt/media/provision.py [qbit jellyfin arr prowlarr bazarr seerr connect homepage]`.
- ⚠️ Каталог `config/<сервис>`, которого нет до `up`, docker создаёт **от root** — Jellyfin/Seerr (uid 1000) падают с `Permission denied`. Новые каталоги создавать заранее или `chown -R rus:rus /opt/media/config`.
- ⚠️ `fallenbagel/jellyseerr` заброшен (последний образ 2025-08) и не логинится в Jellyfin 12 — заменён на `ghcr.io/seerr-team/seerr`.
- Торренты идут **напрямую** с домашнего IP (решение пользователя 2026-10-11; вариант через AWG fr1 отложен). Порт 6881 на роутере **не проброшен** — входящих соединений нет, раздача и скорость хуже.
- **Astra управляет медиацентром** (с 2026-10-11) — скилл `media` в `ruzzy/astra-bot`, команда `/usr/local/bin/media` (поиск/добавление/выбор раздачи по языку/очередь/удаление; изменения — только `--yes` и кнопкой). Ключи — `/etc/media.env` (root:astra 640). ⚠️ Там же копия пароля qBittorrent — при смене общего пароля обновлять вместе с `homepage.env`. В инструкциях Astra `/mnt/backup/media` — исключение из «ничего в /mnt/backup не удалять», но только через `media`. Поле — только чтение.
- Образы на `:latest`, автообновления нет: `cd /opt/media && docker compose pull && docker compose up -d`.
## Агент Astra (с 2026-10-11)
Агент на базе **Codex** (OpenAI), работает на самой малине, управление — из Telegram-бота **@rasp_contol_bot**. Код бота — отдельный репо **`ruzzy/astra-bot`** (там же схема работы).
- Пользователь `astra` (группы `sudo`, `adm`, `systemd-journal`, `users`), пароль заблокирован, **sudo NOPASSWD** (`/etc/sudoers.d/90-astra`) — по решению пользователя. SSH-вход под ним — ключом `~/.ssh/id_ed25519` с Mac (алиас `ssh rpi-astra`).
- ⚠️ sudo = агент видит всё: токен OneDrive (`~rus/.config/rclone/rclone.conf`), пуш-ключ метрик (`/etc/metrics-push/`), deploy-ключ с записью в `infra`, токен бота. Утечка контекста агента = утечка этих секретов.
- **Codex CLI 0.162.1** — полный пакет в `/opt/codex`, симлинк `/usr/local/bin/codex`. ⚠️ Голый бинарник из релиза (`codex-aarch64-…tar.gz`) не годится: `remote-control`/app-server требуют пакет (`codex-package-aarch64-unknown-linux-musl.tar.gz`), иначе «this CLI has no complete local package». Логин — подпиской ChatGPT (`codex login --device-auth`, аккаунт ruzzyrullezz@gmail.com, Plus), токен `~astra/.codex/auth.json` (600). Ключ API не используется.
- **Агенту разрешено пушить в `main` `ruzzy/infra` напрямую** (решение пользователя 2026-10-11, перекрывает правило из `CLAUDE.md` только для него): любые изменения сервера он обязан внести в `servers/rpi.md`/`stacks/` и запушить в том же прогоне, конфликты — `pull --rebase` без `--force`, неразрешимые — отдать пользователю. Источник инструкций — `codex/AGENTS.md` в `ruzzy/astra-bot`.
- Конфиг `~astra/.codex/config.toml`: `approval_policy=on-request`, `sandbox_mode=workspace-write`; инструкции агенту — `~astra/.codex/AGENTS.md` (паспорт, правила infra, что не трогать).
- **astra-bot** (`astra-bot.service`, user `astra`): `/opt/astra-bot/bot.py` ⇄ `codex app-server` (JSON-RPC stdio) ⇄ Telegram long polling. Подтверждения команд/правок — кнопками в чате. Конфиг `/etc/astra-bot.env` (root 600): `TG_TOKEN`, `TG_ALLOWED_IDS=607015,6310034441,129703290` (владелец; Поля — детский профиль; `129703290` — полный доступ как у владельца с 2026-10-12, astra-bot `546b0be`). Уведомления малины (ЭлЖур, роутер) — всем ID из `TG_NOTIFY_IDS` в том же файле (сейчас `607015,129703290` — второй добавлен по его просьбе 2026-10-12; перезапуск не нужен, astra-bot `130b8f8`). Алерты мониторинга — Alertmanager fr1 → @ultimate_log_bot, отдельно. Для `129703290` vault владельца закрыт (правило в инструкциях Astra, не права ФС — Astra root); его заметки — отдельный vault `/home/astra/vaults/129703290/`, создаётся по его просьбе (astra-bot `6218260`). 2026-10-12 исправлен `~astra/.profile` (пустой `if` для `~/.local/bin` ломал login-shell; бэкап `.profile.bak-20261012`). Тред Codex на чат — `~astra/.local/state/astra-bot.json`. Бот лежит в `/opt` под root, а не в `~/infra`, чтобы агент не правил свой код без `sudo`.
- **Приём вложений исправлен 2026-10-11** (раньше бот читал только `text`, фото и подписи молча пропадали): код — `ruzzy/astra-bot`. Фото и JPEG/PNG/WebP-документы до 20 МиБ скачиваются через Telegram `getFile` в `~astra/.local/share/astra-bot/incoming/` (файлы 600, UUID), передаются Codex как `localImage`; подпись и источник пересылки — текстом. Пересланные `/stop` и другие команды читаются как цитата. Во время работы сообщения ставятся в очередь процесса, реальная доставка картинки с подписью и источником подтверждена пользователем 2026-10-11.
- **Пачки изображений добавлены 2026-10-11** (раньше каждый элемент альбома запускал отдельную задачу и отдельное уведомление очереди): установлен [коммит `6276bf8`](https://git.ruzzy.dev/ruzzy/astra-bot/commit/6276bf8). Бот собирает альбом по `media_group_id`/чату/отправителю, быстро пересланные картинки без ID — по чату/отправителю; после 1,5 секунды тишины передаёт все картинки, подписи и источники одним запросом Codex в порядке `message_id`. Служебные уведомления ожидания убраны 2026-10-11 ([коммит `c4e3cd1`](https://git.ruzzy.dev/ruzzy/astra-bot/commit/c4e3cd1)): прежняя проверка пустой очереди повторяла их между пачками; очередь сохранена и работает молча. Проверка включает повторное заполнение опустевшей очереди. Предыдущий код — `/opt/astra-bot/bot.py.bak-20261011-silent-queue`. 23 теста прошли; изменение загружается при перезапуске через `restart-check.py`. ⚠️ Пауза больше 1,5 секунды разделяет серию на пачки; буфер и очередь не переживают перезапуск. Предыдущий код — `/opt/astra-bot/bot.py.bak-20261011-batches`.
- Код бота с доработкой Astra перенесён в `ruzzy/astra-bot` (`f9f6c39`; текущая установленная версия — `c4e3cd1`); временная копия `stacks/astra-bot/` удалена 2026-10-11. Клон — `~astra/astra-bot`. Резервная копия прежнего кода — `/opt/astra-bot/bot.py.bak-20261011-incoming`.
- ⚠️ **Почему не штатный Codex Remote из ChatGPT:** телефон к безголовому Linux не сопрягается — официально хостом может быть только macOS/Windows; `codex remote-control pair` выдаёт код, но и QR, и ручной ввод дают «Pair Failed», до демона попытка даже не доходит (проверено с debug-логом 2026-10-11). Рабочая альтернатива — ChatGPT Desktop на Mac → SSH-хост `rpi-astra`, но тогда Mac должен быть включён.
- `/boot/firmware/cmdline.txt`: добавлен `cfg80211.ieee80211_regdom=RU` (бэкап `.bak-20261011`) — только чтобы `/etc/profile.d/wifi-check.sh` не печатал в stdout login-shell предупреждение про rfkill: оно ломало бы stdio-протоколы (Codex app-server по SSH). Wi-Fi не используется.
- **Доступ к Gitea — пользователь `astra`** (с 2026-10-11, вместо двух deploy-ключей): один SSH-ключ `~astra/.ssh/gitea`, коллаборатор с записью в `ruzzy/infra` (`~astra/infra`) и `ruzzy/astra-bot` (`~astra/astra-bot`), может создавать свои репо в `astra/…` (API-токен `~astra/.config/gitea/token`, 600, scopes `write:repository,write:user`, в Gitea — `astra-rpi`). Почему пользователь, а не deploy-ключи: deploy-ключом нельзя создавать репо, а права коллаборатора видны и меняются в настройках репо. Создавать репо от имени `ruzzy` нельзя — он пользователь, не организация. git identity `Astra (rpi) <astra@rpi.ruzzy.dev>`, host key Gitea (RSA) закреплён. Автопулла нет.
- Профиль Поли (@Polli4ka303), 10 лет, сохранён в инструкциях 2026-10-11: простые короткие ответы без технических терминов; источник [astra-bot `88b88a5`](https://git.ruzzy.dev/ruzzy/astra-bot/commit/88b88a5). Детская печать уточнена: единый PDF → «Печатай» / «Печатай на двух сторонах» → печать без системных кнопок. Доступ добавлен 2026-10-11: Telegram ID `6310034441` внесён в TG_ALLOWED_IDS (ранее только владелец `607015`), профиль закреплён за ID вместо username; [astra-bot `2d761d3`](https://git.ruzzy.dev/ruzzy/astra-bot/commit/2d761d3). Настройка применяется после перезапуска бота.
- Постоянная сессия и журнал Поли добавлены 2026-10-11, [astra-bot `8b3400a`](https://git.ruzzy.dev/ruzzy/astra-bot/commit/8b3400a). После авторизации её чат сохраняется в state.child_chats, тред возобновляется после перезапуска; служебные slash-команды не сбрасывают разговор и не показывают справку администратора. Бот передаёт возраст и текущий chat_id в контекст, отправляет только итоговый ответ. Входящие сообщения, метаданные/пути вложений и текстовые ответы пишутся в ~/.local/share/astra-bot/conversations/<chat_id>.jsonl (файл 600, каталог 700); PDF, отправленный отдельным скриптом, в этот журнал не записывается. Имя пользователя выбирает профиль только после проверки TG_ALLOWED_IDS и не предоставляет доступ. Проверка: 37 тестов; резервный код /opt/astra-bot/bot.py.bak-20261011-child-session. ⚠️ При невозможности resume бот создаёт новый тред; локальный журнал остаётся, автоматического импорта журнала в контекст нет.
## Роутер Huawei HG8245X6 — скилл `router` (с 2026-10-11)
- Роутер — **OptiXstar HG8245X6-10**, оптический терминал Cyta (GPON), ПО V5R022C10S110, `192.168.10.254`. Интернет — PPPoE `1_INTERNET_R_VID_42` (публичный IP динамический, на 2026-10-11 — 46.199.208.45); ещё WAN TR069, VOIP, IPTV. За LAN3 — mesh-точки M15/E15 (Wi-Fi-клиенты за ними видны как `lan3`), на LAN4 — малина.
- Управление — Codex-скилл Astra `router`, код в `ruzzy/astra-bot` (`codex/skills/router/`), команда `/usr/local/bin/router`. Креды веб-интерфейса — `/etc/router.env` (root:astra 640), в git нет. Умеет: статус/WAN, клиенты (IPv4 из аренд DHCP, сигнал Wi-Fi), трафик по интерфейсам, DHCP static add/del, проброс портов add/del (через WAN INTERNET), Wi-Fi — только чтение. Изменения — только `--yes` и кнопкой (`codex/rules/router.rules` разрешает лишь чтение).
- ⚠️ **3 неудачных входа подряд блокируют вход в роутер** (`errloginlockNum = '3'`). `login.cgi` разбирает поля позиционно — порядок как у браузера (UserName, PassWord, Language, x.X_HW_Token), иначе вход «неудачный». login не ретраится. Веб-сервер однопоточный и рвёт соединения при частых запросах — чтение ретраится с паузой; выход (`logout.cgi`) всегда отвечает обрывом, это норма. После каждой операции — выход, чтобы не забивать лимит сессий и не выбивать человека из веб-интерфейса.
- ⚠️ Трафик **по устройствам прошивка не считает** (поля `TrafficRecvRate/SendRate` всегда 0) — только по интерфейсам: `wan` (сумма GEM Ethernet), `iptv`, `lan1..4`, `wifi-2.4`, `wifi-5`.
- ⚠️ Запись Wi-Fi не сделана сознательно: SSID 2.4/5 ГГц связаны (WifiCover/band steering), запрос — десятки полей; ошибка отключит весь дом. Делать отдельно, в согласованное окно.
- `router-watch.timer` (5 мин, root): снимок `/var/lib/router-watch/state.json` (читает astra без сети), `seen.json` (все MAC с первым появлением; база — 22 устройства на 2026-10-11 15:57), `known.json` (имена от пользователя), `events.log`. Новый MAC → Telegram ботом @rasp_contol_bot с флагами (случайный MAC, ночь, без имени, ≥3 новых за час). Метрики `router_*` → pushgateway (job `router`) → алерты группы `home-network`: `RouterWatchStale` (>20 мин), `RouterDownloadSpike`/`RouterUploadSpike` (WAN: 15-мин скорость >4× средней за неделю и >5 МБ/с ↓ / >1 МБ/с ↑, 30 мин). Дашборд https://mon.ruzzy.dev/d/home-net.
- **DLNA и FTP на USB-диске роутера выключены 2026-10-11** (по решению владельца; включали в тот же день с Mac `.17` при настройке диска): DLNA (`X_HW_DmsService`, ShareAllPath, порт 56002) без пароля отдавал всей домашней сети содержимое диска — а там копия OneDrive; FTP (21, root) не нужен. SFTP :8022 (LAN only, WAN выкл) оставлен — через него `router-backup`. Управление — `router usb status|dlna|ftp`.
- Проверено на живом роутере 2026-10-11: чтение всех разделов; `dhcp add/del` (тестовая привязка 02:00:00:00:00:01 → .250 создана и удалена), `portmap add/del` (TCP 59999 → rpi создан и удалён).
- Статическая привязка DHCP `192.168.10.25` ↔ `dc:a6:32:db:b0:f9` (малина) на роутере уже есть — единственная. Привязка `192.168.10.101` ↔ `8e:a9:3a:4e:69:ba` удалена 2026-10-11: устройства с таким MAC нет ни в сети, ни в истории роутера (вероятно, старый телефон со сменившимся случайным MAC).
- Свободная беседа, интернет-поиск, объяснение школьного материала, поиск и генерация картинок для Поли разрешены 2026-10-11; правила установлены в ~/.codex/AGENTS.md, источник [astra-bot `95a2867`](https://git.ruzzy.dev/ruzzy/astra-bot/commit/95a2867). Содержание подбирается для 10 лет, материалы 16+/18+ исключаются; выбранные картинки проверяются перед отправкой. Это инструкции агента, не отдельный сетевой фильтр. Разрешение не распространяется на управление сервером и доступами.
- Голосовые Telegram добавлены 2026-10-11 (ранее отклонялись как неподдерживаемые): [astra-bot `e0fc9dc`](https://git.ruzzy.dev/ruzzy/astra-bot/commit/e0fc9dc), исходники установки и распознавания voice/. Установлен ffmpeg 7.1.5 с зависимостями; python3-venv уже был. Vosk 0.3.45 в /opt/astra-voice/venv, русская модель vosk-model-small-ru-0.22 и helper /opt/astra-voice/transcribe.py, всего 115 МБ. OGG/Opus скачивается через Telegram (600), распознаётся локально без стороннего API, до 5 минут / 20 МиБ, таймаут 180 с; одна запись обрабатывается, до трёх в работе/ожидании, основной цикл бота свободен. Расшифровка идёт в существующий чат, пересылки остаются цитатами; для Поли пишется в журнал. ⚠️ Модель небольшая: шум, имена и сложная речь дают ошибки; пустой результат вызывает просьбу записать заново. Исходные записи автоматически не удаляются. Проверка: 43 теста и настоящий OGG с тишиной (модель запускается, пустая расшифровка); качество живой русской речи ещё предстоит проверить голосовым пользователя. Предыдущий бот — /opt/astra-bot/bot.py.bak-20261011-voice; обновление установлено, перезапуск через restart-check.py после текущего ответа.
- Распознавание голосовых заменено 2026-10-11 на whisper.cpp v1.7.6 + многоязычную base (141 МиБ), [astra-bot `c5c488f`](https://git.ruzzy.dev/ruzzy/astra-bot/commit/c5c488f), установка — `voice/setup-whisper.sh` в репозитории бота. Причина: Vosk услышал «кастовой голосовой» в записи «тестовое голосовое» и поддерживал только русский; Whisper на той же записи получил «тестовый голосовое» (ошибка окончания остаётся). Русский, английский и греческий выбираются автоматически, перевода нет, обработка локальная. Бинарник `/opt/astra-voice/whisper-cli`, модель `/opt/astra-voice/ggml-base.bin`, helper запускается системным Python; установлен CMake 3.31.6 для сборки. Два потока CPU, таймаут бота увеличен с 180 до 900 с. ⚠️ На Pi обработка медленнее, короткие и смешанные фразы могут ошибаться; качество греческой речи ещё не проверено реальной записью. Проверки: 43 теста бота, пользовательский OGG, английский образец JFK распознан правильно, тишина даёт пустой текст. Прежний Vosk сохранён, helper — `transcribe.py.vosk-bak-20261011`, предыдущий бот — `/opt/astra-bot/bot.py.bak-20261011-whisper`. Перезапуск через `restart-check.py` после ответа.
- Индикатор работы добавлен 2026-10-11: [astra-bot `20a8ffc`](https://git.ruzzy.dev/ruzzy/astra-bot/commit/20a8ffc). Раньше typing отправлялся один раз и пропадал через несколько секунд; теперь при распознавании голосовых и выполнении задачи отдельное тихое сообщение с машинкой 🚗 редактируется каждые 4 с, typing обновляется. Один индикатор на чат, перекрывающиеся задачи учитываются; после окончания сообщение удаляется. Проверки: 46 тестов, включая перекрытие задач, редактирование/удаление и неудачную отправку. Резервная копия `/opt/astra-bot/bot.py.bak-20261011-waiting-car`; установка выполнена, перезапуск через `restart-check.py` после ответа.
## Разговорный навык Маруси — отменён и удалён (2026-10-11)
- Подготовленную за день интеграцию (разговорный сервис Astra для колонки Маруся через вебхук VK → nginx fr1 → SSH-туннель → rpi) владелец отменил в тот же вечер. Навык в VK так и не регистрировался.
- Удалено с rpi: `astra-marusia.service`, `astra-marusia-tunnel.service`, `/opt/astra-marusia`, `/etc/astra-marusia.env` (секрет пути вебхука), `/var/lib/astra-marusia` (отдельный CODEX_HOME), ключ туннеля `~astra/.ssh/marusia_tunnel{,.pub}` и `known_hosts_marusia`. На fr1 — vhost, сертификат, sshd-правило, пользователь `marusia-tunnel` (см. `servers/fr1.md`).
- Код удалён и из репозиториев (2026-10-11): `marusia/`, `test_marusia.py` в `ruzzy/astra-bot`, `stacks/astra-marusia/` в `ruzzy/infra` — остаётся в истории git. Если вернёмся к идее — восстановить оттуда; ключ туннеля и пользователь на fr1 — заново.
- Реестр устройств уточнён 2026-10-11 по сообщению владельца: `Xiaomi-Pad-7`, `ce:14:42:fd:a2:d5`, при обнаружении `192.168.10.45` — «Планшет подруги Полины (Xiaomi Pad 7)». Записано через `router known add`; MAC частный, при его смене потребуется новая отметка.
## Obsidian-vault (с 2026-10-11)
- Vault владельца «MyVault» (Obsidian Sync, тариф **Standard**: 1 vault, 1 ГБ, файл ≤ 6 МБ, история 31 день, продление помесячное) — на rpi в `/home/astra/vault` (~60 МБ, 332 файла).
- Клиент — официальный `obsidian-headless` 0.0.14 (`/usr/bin/ob`, npm, Node.js 24 LTS из репо NodeSource — в Debian 13 только Node 20, а клиенту нужен ≥ 22). Вход `ob login` делал владелец (учётка ruzzyrullezz@gmail.com), привязка `ob sync-setup` — vault id `469340a4…`, устройство `rpi-astra`, без E2E-пароля.
- `obsidian-sync.service` (user astra, `ob sync --path /home/astra/vault --continuous`, Restart=always). Режим **bidirectional** (решение владельца: Astra правит заметки по его просьбе); конфиги `.obsidian` не синхронизируются; типы файлов — все (`unsupported` включён).
- ⚠️ Mac владельца не выгружает в Sync «прочие типы» (`.mmd` — 7 диаграмм `Работа/Perps/flows/`): их нет ни в облаке, ни на rpi/iPhone. Лечится на Mac: Настройки → Синхронизация → Другие типы файлов. Суточный архив пока записал их удаление (в истории они есть, коммит `014b2a9`).
- **Архив в git** — `vault-archive.timer` (**каждый час** с 2026-10-11, коммит только при изменениях; было раз в сутки 03:45) → `/usr/local/bin/vault-archive` (`stacks/rpi-backup/`): односторонний commit+push в `ruzzy/MyVault` (продолжает историю git-эпохи; Astra — коллаборатор с записью). Git-каталог отдельно — `~astra/vault.git` с `core.worktree=/home/astra/vault`, в vault нет `.git`. Скрытые файлы не стейджатся (Sync их не приносит — иначе были бы ложные удаления). Первый снимок 2026-10-11 — `014b2a9` (изменения с 2026-09-23). Итог → `backup_*{job="backup_vault"}`, алерты `BackupStale` (>3 ч) и `BackupNeverReported` (3 ч).
- Astra — скилл `obsidian` (`ruzzy/astra-bot`, `codex/skills/obsidian/`): читать свободно, писать только по прямой просьбе, новое — в `Неотсортированное/`, правка — с показом изменений, в стиле владельца (вики-ссылки, без тегов/frontmatter, заметка-папка, картинки в `static/images/`); служебные файлы агента в vault — никогда. Запись в vault вне песочницы Codex → каждая правка через кнопку подтверждения. Проверено 2026-10-11: ответ по заметке «Читаем и слушаем».
- ⚠️ Бот `claude-channels` на fr1 доступа к vault больше не имеет (см. `servers/fr1.md`).
## Камера Xiaomi в домашней сети (2026-10-11)
- `chuangmi.camera.046c04` (линейка Xiaomi Smart Camera, вероятно C300), `192.168.10.129`, MAC `78:df:72:54:ef:bf`, за mesh; в `router-watch` подписана как известная.
- Заводская прошивка: открытых TCP-портов нет (ни RTSP, ни ONVIF, ни веба), только UDP 54321 (miIO); видео и настройки — через облако Xiaomi / Mi Home.
- Интеграцию с rpi владелец снял: просмотр, движение и запись уже даёт Mi Home. Если вернёмся (снимок для Astra/в Telegram, доступ без аккаунта Xiaomi, архив на rpi без облака) — начинать с go2rtc (источник `xiaomi`, нужен вход в аккаунт Xiaomi; проверить поддержку модели). Выгрузка роликов в облако управляется «Обнаружением движения» (MIoT siid 5).
## Субагент Claude Code (с 2026-10-11)
- Пользователь **`claude`** (uid 1002; группы `sudo`, `adm`, `systemd-journal`, `users`, **`astra`**), пароль заблокирован, **sudo NOPASSWD** (`/etc/sudoers.d/91-claude`) — по решению владельца, как у Astra. ⚠️ Это второй агент с root на rpi.
- Почему отдельный пользователь, а не под `astra`: свои клоны и `$HOME` (иначе Codex и Claude Code правили бы одну рабочую копию `~/infra` и ломали друг другу git), отдельная авторизация подписки, видно, кто что сделал.
- Claude Code 2.1.296 — нативный установщик под `claude` (`~claude/.local/bin/claude`, автообновление). Вход — **личная подписка Claude владельца** (`ruz…@gmail.com`, `stripe_subscription`), `~claude/.credentials.json` (600). Подписка общая с Claude Code владельца на Mac (лимиты).
- Gitea-пользователь **`claude`** (id 6, пароль случайный, вход ключом `~claude/.ssh/gitea`), коллаборатор с записью в `ruzzy/infra` и `ruzzy/astra-bot`; клоны `~claude/infra`, `~claude/astra-bot`, коммиты «Claude (rpi)». Прямой push в `main` разрешён, как Astra.
- Доступ к ресурсам Astra — через группу `astra`: `/home/astra` открыт группе (750, было 700) ради vault и снимков; секреты Astra (`~astra/.codex/auth.json`, ключи `~astra/.ssh/*`) — 600, `claude` их не читает (проверено). `/etc/router.env` — `root:astra 640`.
- Вход с Mac: `ssh rpi-claude` (ключ владельца в `~claude/.ssh/authorized_keys`).
- Мост **`ask-claude`** (`/usr/local/bin/ask-claude`, исходник — `ruzzy/astra-bot` `codex/skills/claude-code/scripts/`): Astra запускает `claude -p … --output-format json --dangerously-skip-permissions` через `sudo -u claude`; `--bg` — фоном, до 3 одновременно, тайм-аут 60 мин; `status` / `result` / `wait`. Журнал — `/var/lib/ask-claude/runs/<ID>/` (`claude:astra 2770`). ⚠️ Подсчёт идущих задач — по `/proc/<pid>`, не `kill -0` (процесс чужого пользователя → EPERM).
- Инструкция субагента — `~claude/.claude/CLAUDE.md` (исходник `codex/claude/CLAUDE.md`): правила хоста/infra/vault, не работать в клонах Astra, отчёт в конце. Скилл Astra — `claude-code` (делегирует по просьбе и сама при распараллеливании; каждый запуск — через кнопку подтверждения).
- Проверено 2026-10-11: две фоновые задачи параллельно (место на дисках, таймеры) — обе код 0, 4 и 8 с.
## Сетевая папка для Mac — Samba, только чтение (с 2026-10-11)
- Зачем: смотреть бэкап OneDrive (фото «Пленки» и пр.) с Mac в Finder: «Подключиться к серверу» → `smb://rpi.local` (или `smb://192.168.10.25`).
- Шары: **`onedrive`** → `/mnt/backup/onedrive/current` (зеркало OneDrive), **`onedrive-archive`** → `/mnt/backup/onedrive/archive` (удалённое/перезаписанное). Обе **`read only = yes`** — бэкап с Mac не стереть.
- Доступ: только `rus`, пароль Samba отдельный от системного (`sudo smbpasswd -a rus`, задаёт владелец); гостей нет (`map to guest = never`, `restrict anonymous = 2`). Слушает только `192.168.10.25:445` и loopback (`bind interfaces only`), `hosts allow 192.168.10.0/24`; SMB ≥ 2.1; NetBIOS (`nmbd`) выключен, Mac находит по mDNS (`multicast dns register`). macOS-совместимость — `vfs objects = catia fruit streams_xattr`.
- Конфиг — `stacks/rpi-share/smb.conf` → `/etc/samba/smb.conf` (исходный Debian — `smb.conf.orig`).
- ⚠️ Где фото: живая фотопленка телефона идёт в OneDrive `Изображения/Пленка/<год>/<месяц>` (4393 файла, 39 ГБ на 2026-10-11); `Ruslan/Фотокамера` — разовая выгрузка 2022 г. (121 снимок 2017–2018), `Olya/Фотокамера` (5921, последний — 2025-11).
## Электронный дневник ЭлЖур (с 2026-10-12)
- Школа Morfosis, `https://morfosis.eljur.ru`, ученица 4В-К (`u.2701`). Родительская учётка — `/etc/eljur.env` (root:astra 640), в git нет.
- Скилл Astra **`eljur`** (`ruzzy/astra-bot`, `codex/skills/eljur/`), команда `/usr/local/bin/eljur`: `hw [день]`, `week [день]` (расписание + ДЗ), `grades [предмет]` (все оценки по предмету с датами и весом), `avg`, `table`, `final [--year]` (итоговые за триместры и годовая, `/journal-student-resultmarks-action`), `watch`; у `grades`/`avg`/`table` — `-t I|II|III` или `--from/--to` (промежуток — со всех триместров). Чтение — без подтверждения (`codex/rules/eljur.rules`, плюс бот сам подтверждает `eljur` hw/week/grades/table/avg/final); в ЭлЖуре ничего не меняется. **Поле** всё это тоже доступно (владелец 2026-10-12).
- API для родителя у ЭлЖура нет — разбор страниц: вход `POST /ajaxauthorize` (multipart: `username`, `password`, `return_uri`) → JWT-кука `jwt_v_2`, кэш сессии `/var/lib/eljur/cookies` (root:astra 2770/660). Дневник — `/journal-app/u.2701/week.N`; ⚠️ **знак обратный: `week.-1` — следующая неделя, `week.1` — прошлая**. Оценки — `/journal-student-grades-action/u.2701` (ячейки `name`=предмет, `id` — уникален только внутри предмета, не путать с `data-lid`, `mark_date`, `weight` 1–3, `cell-data`).
- **Таблицы** — `eljur table [-t I|II|III] [--from Д --to Д]`: компактная (~32 знака) таблица 5/4/3/2/Н/средний по предметам в блоке ```…```. Бот Astra с 2026-10-12 рендерит такие блоки моноширинно (HTML `<pre>` + `parse_mode`, при отказе Telegram — обычный текст; `astra-bot` `a9c43e2`, бэкап прежнего `/opt/astra-bot/bot.py.bak-20261012-tables`). Markdown-таблицы `| a | b |` Telegram не рисует — бот сам переводит их в выровненный `<pre>` (`777804c`); жирный/курсив/ссылки/списки тоже конвертируются в HTML (`a0c8952`).
- ⚠️ **Триместры:** границы — из JSON страницы (`sp_start`/`sp_end`, полночь по Кипру в UTC): I 01.09–27.11, II 01.12–26.02 (2026/27). Страница триместра `/sp.II%20триместр` отдаёт и оценки вне периода (II до начала показывает сентябрьские) — фильтруем по границам.
- ⚠️ Колонка «Средняя» на сайте пустая в HTML (её досчитывает JS) — средний балл считаем сами: взвешенный, «Н» не учитывается, «Общий» — среднее по предметам. На 2026-10-12: общий 4.88.
- **Уведомления о новых оценках** — `eljur-watch.timer` (каждые 30 мин, 07–22) → `eljur watch` (root, токен бота) → Telegram ботом @rasp_contol_bot в чат 607015. Состояние — `/var/lib/eljur/grades.json`; первый запуск 2026-10-12 запомнил 105 оценок без уведомления; в тот же день ключ оценки исправлен (раньше склеивал оценки одного предмета через `data-lid`) и состояние пересоздано (старое — `grades.json.bak-20261012`).
## Подтверждения Astra — критерии (с 2026-10-13, astra-bot fdeae21)
- Владелец: «напрягает на каждый чих». Разбор сессий Codex за 5 дней: ~75 кнопок на `git` (в песочнице `.git` всегда read-only), ~50 на правки `~/astra-bot` (вне workspace), ~20 на `systemctl`/`journalctl`.
- Теперь без кнопки: `~/.codex/rules/base.rules` — git в своих репо (status…pull/commit/push; `push --force/-f/--mirror/--delete` — **forbidden**), `systemctl status/show/list-*`, `journalctl`, `sudo ls/du/smartctl -A`, `docker ps/logs`; `config.toml` `[sandbox_workspace_write] writable_roots = ["/home/astra/astra-bot", "/home/astra/vault"]` (vault — решение владельца: откат через историю Sync и `vault.git`).
- По-прежнему кнопкой: системные изменения (sudo install/cp/rm, systemctl restart/enable, apt, /etc, /opt, /usr/local), удаление вне /tmp, действия скиллов с последствиями. ⚠️ `network_access = false` сознательно: агент читает почту/чаты — внедрённая инструкция не должна без кнопки вывести данные наружу; `sudo cat/tail` не разрешены (секреты).
- Прежние `config.toml`/`AGENTS.md` — `~astra/.codex/*.bak-20261013-approvals`. Проверено `codex execpolicy check` и `codex sandbox` (запись в оба каталога есть, сети нет).
- ⚠️ Грабли: ставя `AGENTS.md` из своего клона, можно затереть свежий коммит Astra (так 2026-10-13 чуть не потерялась её заметка про песню Поли) — сначала `git pull`, потом установка.
## Рабочий помощник — скилл `work` (с 2026-10-12)
- Рабочие задачи/записи владельца — в его vault, папка `Работа/` (отдельный vault не заводили: Sync Standard = 1 удалённый vault): задачи `Работа/TODOшки.md` (его прежний список; формат Tasks `- [ ] … 📅 ГГГГ-ММ-ДД ⏰ ГГГГ-ММ-ДД ЧЧ:ММ`, выполненные `✅`), `Входящие.md`, `Дневник/ГГГГ-ММ-ДД.md`, `Встречи/ГГГГ-ММ-ДД Тема.md` (папки — с заметкой-папкой).
- **Проекты** (с 2026-10-12, astra-bot dae16bc): заметка на проект `Работа/Проекты/Имя.md` — разделы Контекст / Планы / Проблемы (чекбоксы, найденные баги и риски) / Журнал; задачи проектов попадают в `work list` и утреннюю сводку (+ счётчик открытых проблем). Дневник ссылается на проект полным путём (`[[Работа/Проекты/Perps|Perps]]` — у владельца уже есть одноимённые справочные заметки).
- `/usr/local/bin/work` (код — `ruzzy/astra-bot` 1173d33, `codex/skills/work/`); только владелец (`--by 607015`, иначе отказ), без кнопок (`work.rules`) — пишет только в перечисленные места.
- Таймеры root: `work-digest.timer` — будни 09:00 Asia/Nicosia (задачи просрочено/сегодня + рабочая занятость из `gcal agenda` + счётчик входящих); `work-remind.timer` — каждую минуту, задачи с ⏰ (досылает до 30 мин опоздания; дедуп `/var/lib/astra-work/reminded.json`).
- Решение владельца: на личной инфре — только его рабочие пометки, без паролей/токенов/данных клиентов/выгрузок прода; рабочие системы (почта, Slack, GitLab, grafana walletteam) не подключаются. Оле — закрыто.
## Открытые хвосты (на 2026-10-11)
- USB3-хаб с питанием — теперь только ради скорости: WD Elements 1 ТБ работает второй копией в роутере (~10 МБ/с); на малине через хаб было бы ~100 МБ/с (обновлено 2026-10-11, было «копия одна»).
- Нет node-exporter (CPU/RAM/диск самой малины в Prometheus не видны) — пуш через pushgateway для node-метрик неудобен; решить, нужен ли.
- Первый прогон бэкапа стартовал до появления `ExecStopPost` — его итог может не дойти в метрики; `backup_last_success_timestamp` появится с первого прогона по таймеру. До этого `BackupStale` молчит (серии нет).
- ⏳ `codex-remote.service` (демон `codex remote-control` с `RUST_LOG=debug`) всё ещё включён и работает, хотя сопряжение не взлетело — убрать или оставить ждать поддержки Linux от OpenAI (решение за пользователем).
- Токен бота передавался через чат с ассистентом — при желании перевыпустить в @BotFather (`/revoke`) и вписать в `/etc/astra-bot.env`.
- ⚠️ **Закрытый ключ age бэкапа серверов — одна копия, на Mac.** Положить вторую вне малины и вне Mac (менеджер паролей/распечатка), иначе потеря Mac = бэкапы fr1/ru1 бесполезны.
- Разово скопировать на малину `~rus/backups` (5.4 ГБ) и `~tgclaude/backups` (4.2 ГБ) с fr1 — тогда их можно убрать с fr1.
- Пробное восстановление бэкапа серверов — раз в квартал (следующее ~январь 2027), см. `stacks/server-backup/README.md`.
- Через неделю снять SMART и посчитать реальный прирост Start_Stop/Load_Cycle — прогноз ресурса пока оценочный.
- ⏳ Определить хранение входящих фото: сейчас автоматически не удаляются ради ссылок в старых тредах; очередь сообщений только в памяти и теряется при перезапуске.
- Медиацентр: проброс 6881 tcp/udp на роутере (раздача/скорость); пароль SFTP роутера слабый (только LAN) — сменить; Kinozal и OpenSubtitles — аккаунты пользователя; решить про автообновление образов.
- Ложный алерт PushMetricsStale для backup_servers разобран 2026-10-11: live Result=success, завершение 15:14:12, ExecStopPost завершён 15:14:15, локальный backup_servers.success есть; SMART и router-watch успешно пушат, health fr1 видит свежие метрики. Ошибка в правиле fr1: общий порог 3 ч применялся к дневному бэкапу. В infra подготовлено ограничение PushMetricsStale на job=smart; BackupStale/BackupNeverReported сохранены. Применение/reload выполняет агент fr1; сами бэкапы не перезапускались.
- 2026-10-11 по просьбе владельца включено Telegram-уведомление Sonarr после импорта серий Chernobyl (HBO, 2019, TVDB 360893) в чат владельца 607015. Ранее уведомления о загрузках не были настроены. Connect `Telegram: Chernobyl owner`, только onDownload, ограничение тегом `notify-owner-chernobyl`; другие сериалы не затрагиваются. Повторяемая настройка — `stacks/rpi-media/scripts/notify-chernobyl.py`, запуск root; токен берётся из `/etc/astra-bot.env`, хранится также в локальной БД Sonarr, в git отсутствует. ⚠️ Уведомление означает импорт отдельной серии, не подтверждает английские субтитры или готовность всего сезона; подходящая раздача при настройке пока не найдена. Настройка проверена чтением API, фактическая доставка будет проверена при импорте.
- 2026-10-11 Chernobyl импортирован Sonarr (5/5), но Jellyfin показывал 0 сериалов: автообнаружение/Connect не обновили каталог. POST `/Library/Refresh` через существующий API-ключ обновил каталог, проверено `media status`: 1 сериал, 5 серий. Refresh конкретного ItemId библиотеки с Recursive=true новых серий не обнаружил; для этой ситуации нужен полный скан. Причина сбоя Connect пока не установлена; конфиги и файлы медиатеки не менялись.
- 2026-10-12 по прямой просьбе владельца OneDrive-бэкап и еженедельная проверка исключают объекты с метаданными `malware-detected=true` через `--metadata-exclude`; `--onedrive-av-override=false` явно запрещает обход серверного антивируса независимо от конфигурации. Ранее восемь заблокированных файлов приводили к ошибке всех прогонов и нулевой метке последнего успеха. Фильтр динамический, не список восьми путей; проверен на `Olya/laptop/калитники - платформы.rar`: без фильтра файл виден, с фильтром отсутствует; метаданные подтверждают true. Скрипты установлены в `/usr/local/bin/`, прежние сохранены с `.bak-20261012-malware`. Файлы в current/archive и в облаке не удалялись, текущие процессы не прерывались; следующий запуск по таймеру использует фильтр. ⚠️ Успех означает полноту бэкапа только среди файлов без отметки OneDrive; ранее сохранённые копии не очищаются, новые реальные ошибки не подавляются. Успешный полный прогон после изменения пока не подтверждён.
- 2026-10-12 08:16 MSK по прямой просьбе владельца вручную запущен `onedrive-backup.service` после установки фильтра malware-detected. Перед запуском служба была failed (предыдущий результат 04:57), конкурирующего прогона не было; запуск принят, состояние activating/start. Итог и обновление метрики успеха ещё ожидаются, алерт вручную не сбрасывался.
## Google: календарь и почта владельца (с 2026-10-12)
- Скилл Astra **`google`** (`ruzzy/astra-bot`, `codex/skills/google/`): `gcal` (повестка, свободные окна, создать/перенести/изменить/удалить событие), `gmail` (**только чтение**: список/поиск, непрочитанные, письмо, вложения → `/tmp/astra-gmail/`), `google-auth` (вход/статус). На rpi: `/usr/local/bin/{gcal,gmail,google-auth}`, модуль `/usr/local/lib/astra-google/google_api.py` (только stdlib). Без подтверждения — `codex/rules/google.rules`.
- Аккаунт `ruslan.gilfanov@gmail.com`; OAuth-клиент Desktop в Google Cloud проекте `astra` (Calendar API + Gmail API), приложение опубликовано (In production, не проверено Google — для личного использования). Branding: домашняя `https://www.ruzzy.dev/astra/index.html`, политика `https://www.ruzzy.dev/astra/privacy.html` (fr1, `stacks/www-ruzzy/`), authorized domain `ruzzy.dev`, без логотипа (логотип требует верификации).
- Права токена: `calendar` + `gmail.modify` (с 2026-10-12 вместо `gmail.readonly` — ради «прочитано»; по правилам владельца других изменений почты нет, `gmail mark-read` — только по прямой просьбе, с кнопкой). Секреты — `/etc/astra-google/` (root:astra 2770; `client.json` 640, `token.json` 640 — refresh-токен), в git нет. Вход (PKCE, redirect `http://localhost`): `google-auth url` → «Разрешить» → адрес `http://localhost/?code=…` из браузера → `google-auth code 'АДРЕС'`. Отозвать — myaccount.google.com/permissions.
- Календари: личный (запись), «Семейная группа» (запись — только по явной просьбе), праздники Кипра (`ru.cy#holiday`, чтение; США убраны 2026-10-12), **рабочий `rgilfanov@walletteam.org` — только занятость** (freeBusyReader через `freeBusy`): админ walletteam запрещает внешний доступ к подробностям; обход (секретный iCal, копирование) — не делать. `gcal` пишет только в календари с ролью owner/writer.
- **Правила записи (владелец 2026-10-12):** менять календарь по запросу могут владелец и Оля (`129703290`); `gcal` пишет **только в основной личный** и **только в свободное время** (add/move отказывают при пересечении с любым событием, включая рабочую занятость). Каждое изменение — с `--by <TG_ID>`; журнал `/var/lib/astra-google/changes.log`; изменения Оли → оповещение владельцу через очередь `/var/lib/astra-google/outbox/` (astra не видит токен бота; отправляет таймер root). Состояние — `/var/lib/astra-google/` (root:astra 2770).
- **Напоминания владельцу** (только 607015): `gcal-remind.timer` (каждую минуту, root) — «⏰ Через 5 мин» для всех событий личного и семейного календарей (рабочая занятость и праздники — нет), дедуп `reminded.json`; `gcal-digest.timer` (10:00 Asia/Nicosia) — важные события завтра (врач/стоматолог/анализы/парикмахерская/поездки/рейсы/отели — по словам, или флаг `--important` в `extendedProperties.private.important`). Юниты — `codex/skills/google/systemd/` в astra-bot. Проверено 2026-10-12: тестовое событие от Оли → оповещение в 10:02 и напоминание за 5 мин в 10:03.
- Доступ: владелец — всё; Оля (`129703290`) — календарь (чтение, запись в личный в свободное время), почта закрыта; Поле — ни календарь, ни почту (правила в `codex/AGENTS.md`).
### Почта: индекс, классы, выгрузка в заметки (с 2026-10-12)
- Скилл **`mail`** (`codex/skills/mail/`), `/usr/local/bin/mail`: индекс «Входящих» и «Спама» в `/var/lib/astra-mail/mail.db` (SQLite + FTS5, astra 2770/660) — поиск по отправителю/теме/тексту/вложениям, классы **важное / обычное / рассылка / спам** с причиной (правила владельца → Gmail «Спам» → адресаты «Отправленных» и смысловые признаки → List-Unsubscribe/категории Gmail → обычное). В Gmail ничего не меняет.
- `mail-sync.timer` (astra, раз в 5 мин после окончания прошлого запуска): `history.list` по `historyId`; если история устарела — полная пересинхронизация. ⚠️ **Квота Gmail проекта `astra` — ~100 писем `messages.get(full)` в минуту** (замерено: 403 «Units per minute per user» после ~105 запросов) → скорость ≤ 1,5 запроса/с; первичная индексация ~12,4 тыс. писем ≈ 2+ часа (запущена 2026-10-12).
- Первичная индексация завершена 2026-10-12 (~16:30): **12 447 писем**, 259 контактов. Грабли: ~500 писем, скачанных в прерванном прогоне до «отметить всё прочитанным», остались в индексе непрочитанными (полная синхронизация не перекачивает уже имеющиеся) → добавлена сверка `mail sync --unread` (и в конце полной), astra-bot b501d6d. Классы после настройки: важное 1822 / обычное 4700 / рассылка 5865 / спам 60; автоматические адреса (no-reply, notifier, hello@, info@…) не считаются «людьми», Gmail-«важное» для них → обычное.
- С 2026-10-12 (astra-bot db247c9) в индексе и **«Отправленные»** — класс «отправленное» (855 писем), в полнотекстовом поиске адресаты (миграция fts — пересоздание и `rebuild` при первом запуске), фильтр `--to`. Докачка +872 письма ≈ 10 мин; бэкап базы до миграции — `mail.db.bak-20261012-sent`, прежний скрипт — `/usr/local/bin/mail.bak-20261012-sent`. Итого 13 319 писем.
- **Ожидаемые письма** (с 2026-10-13, astra-bot 3995320): `mail watch add АДРЕС|@домен [--once] [--note]` — новое письмо (в т.ч. в спаме) при инкрементальной синхронизации → файл в `/var/lib/astra-google/outbox/` → `gcal remind` (root, раз в минуту) шлёт владельцу; задержка ≤ ~6 мин. При полной пересинхронизации не оповещает (чтобы не завалить старым). Действует: nellka@inbox.ru (постоянно). Прежний скрипт — `/usr/local/bin/mail.bak-20261013-watch`.
- **Выгрузка в заметки**: `mail export add АДРЕС ПАПКА` — письма адреса (вся почта) → заметки в `~astra/vault/ПАПКА/` (текст без изменений, вложения в `ПАПКА/attachments/<заметка>/`), проверка при каждом sync. Действует **nellka@inbox.ru → `Nelka/`** (193 письма 2014–2026, ~330 МБ вложений; Obsidian Sync принял, в т.ч. файлы > 5 МБ). Заметки попадают и в почасовой архив vault (`vault.git` → gitea).
- 2026-10-12 по просьбе владельца напоминания за 5 минут включены и для рабочей занятости (ранее только личный/семейный). Установлен astra-bot `cc6a4f8`, код и юнит — в `codex/skills/google/`; таймер каждую минуту без перезапуска бота. Работа читается только freeBusy, сообщение «Занят (работа)», подробности не запрашиваются; праздники исключены, утренний дайджест не изменён. ⚠️ FreeBusy объединяет соседние/пересекающиеся встречи в один интервал: напоминание к началу интервала, не каждой скрытой встрече. Запрос за полные дни сохраняет исходное начало и не напоминает о уже идущей встрече; дедуп включает календарь и начало. Проверены будущий рабочий интервал, пропуск идущего, отсутствие повторной отправки, 52 теста бота прошли. Предыдущий gcal сохранён `/usr/local/bin/gcal.bak-20261012-work`.
- 2026-10-12 по просьбе владельца в постоянный профиль Поли добавлена заметка о необходимости выучить песню к Новому году (ссылка сохранена в инструкциях); ранее заметки не было. astra-bot `9009e9f`, `codex/AGENTS.md` установлен в `~/.codex/AGENTS.md`. Срок и автоматические уведомления не назначались. ⚠️ Инструкции подхватываются новым разговором, существующая постоянная сессия ребёнка может сохранять прежний контекст.
- 2026-10-12 по просьбе владельца скилл eljur изменён: общий запрос домашки показывает 14 календарных дней начиная с завтра, затем остальные даты по порядку; ранее период по умолчанию не был задан. Явные даты/диапазоны сохраняются. Данные запрашиваются для всех пересекающихся недель, пустые записи не выдаются за отсутствие будущего ДЗ. SKILL.md сохранён в astra-bot и установлен в ~/.codex/skills/eljur/; CLI и школьные данные не изменялись.
- 2026-10-12 уточнён формат ДЗ по просьбе владельца: скилл eljur полностью пропускает выходные, даты и предметы без заданий (ранее перечислял пустые дни). Диапазон по умолчанию остаётся 14 дней с завтра; если весь диапазон пуст, одна короткая фраза. Изменение в astra-bot `codex/skills/eljur/SKILL.md`, установлено в ~/.codex/skills/eljur/; школьные данные не менялись.
- 2026-10-12 по просьбе владельца общие задания/напоминания из профиля (например, песня без срока) исключены из ответов о домашке; ранее могли добавляться отдельным пунктом. Скилл eljur теперь показывает только датированные задания дневника, общие задания — по отдельному явному запросу. astra-bot `55ab697`, SKILL.md проверен валидатором и установлен в ~/.codex/skills/eljur/.
- 2026-10-13 в постоянный профиль Поли по просьбе владельца добавлена вторая песня для изучения, предыдущая сохранена. astra-bot `33fb800`, codex/AGENTS.md установлен в ~/.codex/AGENTS.md. Срок не задан; общие песни не включаются в ответы с домашкой ЭлЖура. Существующая сессия может не подхватить новые инструкции сразу.