Files

170 lines
14 KiB
Markdown
Raw Permalink 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.
# WALKTHROUGH — GoToSocial (капитанский журнал)
Хронология реализации. Цель — воспроизводимость с нуля.
## 2026-09-06 — Инициализация проекта
### Задача
Развернуть GoToSocial (федiverse-нода ActivityPub) в Docker: /opt/gotosocial, OpenSpec workflow, первое change на развёртывание, git (gitverse истина + gitea зеркало), бэкап на Яндекс.Диск, мониторинг — post-MVP.
### Решения пользователя (ответы на вопросы)
- БД: SQLite
- Схема: split-domain (host=social.dedinit.ru, account-domain=dedinit.ru), доступ к созданию файлов на apex есть
- Имя инстанса: dedinit.ru; admin: estorozhenko; название сервера: dedinit(); описание: «Нода для обычных людей»; email: kpa39l@yandex.ru
- SMTP: smtp.jino.ru:587 STARTTLS, ящик social@dedinit.ru (пароль в .env)
- Бэкап: SQLite consistent + медиа
- Мониторинг бэкапов/дашборд Grafana/импорт из Telegram — post-MVP
- Метрики/логи: отдельный job (не включать в общий)
- vps02 — только прокси, сервис на bigbox
- Git: последний стабильный тег с фиксацией, ручное обновление
- Секреты в .env, в git исключён
- Первый коммит — сразу с начальными изменениями (не ждать готового сервиса)
### Исследование
- Документация установки в контейнере (v0.22.1): https://docs.gotosocial.org/en/v0.22.1/getting_started/installation/container/
- Скачаны refs: ref-container.md, ref-docker-compose.yml, ref-host-account-domain.md, ref-metrics-config.yaml, ref-metrics.md, ref-observability.md, ref-storage.md
- Официальный example docker-compose: https://codeberg.org/superseriousbusiness/gotosocial/raw/branch/main/example/docker-compose/docker-compose.yaml (важно: путь НЕ example/docker-compose.yml — это 404)
- Метрики: OpenTelemetry → Prometheus: `metrics-enabled: true` + env `OTEL_METRICS_PRODUCERS=prometheus`, `OTEL_METRICS_EXPORTER=prometheus`. Эндпоинт /metrics: go_goroutines, gin_, bun_*
- Логи: log-format: json → promtail → Loki
- Split-domain: аккаунты @user@dedinit.ru при host=social.dedinit.ru; требует .well-known редиректы на apex; НЕОБРАТИМО после старта
### Инфраструктура (проверено)
- bigbox (10.8.0.2 через WG): сервисы в /opt/<svc>, стек мониторинга (Prometheus :9090, Grafana :3001, Loki :3100, node_exporter + textfile /var/lib/prometheus/node-exporter/)
- vps02 87.242.100.206: Caddy 2 (контейнер caddy:alpine, host net), слушает 80/443, паттерн `домен { reverse_proxy 10.8.0.2:<port> }`
- SSH vps02: `ssh -i /mnt/yandex-disk/.ssh_box/cloudruVPS estorozhenko@87.242.100.206` (sudo работает); root/другие ключи НЕ подходят
- Яндекс.Диск: mount /mnt/yandex-disk, бэкапы в /mnt/yandex-disk/backup/<svc>-backups/
- DNS: social.dedinit.ru A → 87.242.100.206 (создан пользователем), apex dedinit.ru → 81.177.135.38 (Jino)
### OpenSpec workflow
```bash
cd /opt/gotosocial
openspec init --tools hermes --force --no-animation # CLI 1.12.0, бинарь /opt/hermes/.hermes/node/bin/openspec
openspec new change deploy-gotosocial
```
- config.yaml (context + rules) — инфра-специфичный
- proposal.md, specs/gotosocial-deploy/spec.md + specs/gotosocial-backup/spec.md, design.md, tasks.md
- **Питфол:** OpenSpec требует `#### Scenario:` блоки (GIVEN/WHEN/THEN) ВНУТРИ каждой requirement — просто Verification-блок не проходит validate. Исправлено, `openspec validate deploy-gotosocial` → valid.
### Рабочие файлы
- docker-compose.yml: image superseriousbusiness/gotosocial:v0.22.1 (фикс. тег), container_name gotosocial, user 1000:1000, restart unless-stopped, env OTEL_METRICS_*, env_file .env, ports 8080:8080, volumes ./data:/gotosocial/storage + ./config.yaml:/gotosocial/config.yaml:ro, network gotosocial 172.29.0.0/16
- config.yaml: host social.dedinit.ru, account-domain dedinit.ru, protocol https, port 8080, trusted-proxies [127.0.0.1/32, ::1, 172.29.0.1/16], log json, db-type sqlite, db-address /gotosocial/storage/sqlite.db, storage-local-base-path /gotosocial/storage, SMTP smtp.jino.ru:587 (пароль пустой в config — берётся из .env? **TODO: проверить, как GtS читает пароль из env, когда в config.yaml пусто**), metrics-enabled true, instance-title dedinit(), instance-description «Нода для обычных людей», instance-contact-email kpa39l@yandex.ru
- .env: GTS_SMTP_PASSWORD='fF@4guOf9spE2#8$'
- backup.sh: VACUUM INTO → tar.gz (sqlite+storage) + отдельный .env-архив; копия /mnt/yandex-disk/backup/gotosocial-backups/; ротация локально 7д, ЯД 30д; mountpoint-проверка ЯД, exit 1 при ошибке
- README.md, .gitignore (.env, data/, backups/, refs/, openspec/changes/archive/)
### Git
```bash
cd /opt/gotosocial
git init -b main && git add -A
git -c user.name=estorozhenko -c user.email=kpa39l@yandex.ru commit -m "openspec: change deploy-gotosocial — развертывание GoToSocial (compose+config+backup+docs)"
# d385d8b
```
### Создание репозитория gitverse (ВАЖНО — питфолы)
- SSH-ключ gitverse работает: `ssh -i /mnt/yandex-disk/.ssh_box/gitverse git@gitverse.ru` → «Hi kpa39l!»
- push-to-create НЕ включён для пользователей: «Push to create is not enabled»
- Токен из obsidian vault (Gitverse mirror token.md, 40 hex) НЕ работает для API gitverse (400 на всё) — это токен gitea-зеркала
- **Правильный способ — скилл git-forge-management + reference gitverse-api.md:**
- Токен: `source /opt/hermes/.hermes/secrets/git-tokens.env` → GITVERSE_PAT, GITVERSE_API, GITVERSE_LOGIN=kpa39l
- Accept header ОБЯЗАТЕЛЕН: `application/vnd.gitverse.object+json;version=1` (без него 400)
```bash
curl -s -X POST "${GITVERSE_API}/user/repos" \
-H "Authorization: Bearer ${GITVERSE_PAT}" \
-H "Accept: application/vnd.gitverse.object+json;version=1" \
-H "Content-Type: application/json" \
-d '{"name":"gotosocial","description":"...","private":false,"auto_init":false}'
# HTTP 201, id 327796, full_name kpa39l/gotosocial
```
### Push
- Логин аккаунта gitverse = **kpa39l** (не estorozhenko!)
```bash
git remote add origin "https://kpa39l:${GITVERSE_PAT}@gitverse.ru/kpa39l/gotosocial.git"
git push -u origin main
```
- **Питфол:** токен остался зашит в .git/config — убрано: `git remote set-url origin https://kpa39l@gitverse.ru/kpa39l/gotosocial.git` (creds из git-tokens.env/credential helper)
### Gitea pull-mirror
```bash
source /opt/hermes/.hermes/secrets/git-tokens.env
curl -s -X POST "${GITEA_API}/repos/migrate" \
-H "Authorization: token ${GITEA_TOKEN}" -H "Content-Type: application/json" \
-d "{\"clone_addr\":\"https://oauth2:${GITVERSE_PAT}@gitverse.ru/kpa39l/gotosocial.git\",\"repo_name\":\"gotosocial\",\"repo_owner\":\"estorozhenko\",\"service\":\"git\",\"mirror\":true,\"mirror_interval\":\"8h\"}"
# HTTP 201, mirror=true; затем POST /repos/estorozhenko/gotosocial/mirror-sync → 200
```
- **Питфол:** migrate с ssh:// НЕ работает (422 protocol not allowed) — только HTTPS + oauth2:PAT (проверено ранее, см. reference gitea-pull-mirror-gitverse.md)
### Команды развёртывания (следующий шаг)
```bash
cd /opt/gotosocial && docker compose up -d
docker compose exec gotosocial gotosocial admin account create \
--username estorozhenko --email kpa39l@yandex.ru \
--password '<пароль>' --confirmation
docker compose exec gotosocial gotosocial admin account promote --username estorozhenko
```
Caddy vps02: блок `social.dedinit.ru { reverse_proxy 10.8.0.2:8080 }`, `docker exec caddy caddy reload --config /etc/caddy/Caddyfile`
## Открытые вопросы / риски
- Как GtS читает SMTP-пароль: config.yaml `smtp-password: ""` + .env `GTS_SMTP_PASSWORD` — проверить при первом запуске, что письма уходят
- Пароль админа estorozhenko — нужен от пользователя
- Доступ к vps02 для правки Caddyfile — нужен (SSH estorozhenko@87.242.100.206 + cloudruVPS ключ)
- .well-known редиректы на apex (Jino) — подготовить правила
## 2026-09-13 — Почта: пустой From → спам (причина и фикс)
### Симптом
- Письма GoToSocial (подтверждение регистрации и т.п.) уходили на внешние ящики (Яндекс) в СПАМ.
- Заголовки: `From: <>` (пустой) — письмо без отправителя.
- X-Yandex-Spam: 4 (pessimistic).
### Диагностика
1. SMTP Jino напрямую (python smtplib): логин social@dedinit.ru OK, RCPT kpa39l@ya.ru OK, DATA → `250 Ok: queued`. SMTP-стек рабочий.
2. DNS dedinit.ru: SPF и DMARC ОТСУТСТВОВАЛИ; DKIM `default._domainkey` был.
3. Исходники GtS (/tmp/gts-src, v0.22.1), state.go:100-101: флаг `--smtp-from` → env `GTS_SMTP_FROM`.
4. В docker-compose.yml стоял **неверный** env: `GTS_SMTP_FROM_ADDRESS` (такого ключа нет → From пустой).
### Фикс
```bash
# docker-compose.yml: GTS_SMTP_FROM_ADDRESS: "..." → GTS_SMTP_FROM: "social@dedinit.ru"
cd /opt/gotosocial && docker compose up -d --force-recreate gotosocial
```
### Питфол — systemd1 D-Bus завис
- `docker compose up` завис: systemd1 D-Bus таймаут 25s, контейнер остался `Created`.
- Обход: `/etc/docker/daemon.json` → `"native.cgroupdriver": "cgroupfs"`, `systemctl restart docker` (вручную, минуя systemd1).
- **Риск:** после перезагрузки bigbox dockerd может не подняться сам (systemd1 фризит). Проверить при следующем ребуте. (Другой вариант — перезапуск systemd как PID 1; не делали.)
### Проверка
- `docker exec gotosocial /gotosocial/gotosocial admin account list` — kunikov был unconfirmed (unconfirmed_email=kpa39l@ya.ru), подтверждён вручную `admin account confirm kunikov`.
- Внешний https://social.dedinit.ru → 200; письма теперь от social@dedinit.ru (непустой From).
### Openspec (posthoc)
- change `fix-smtp-from-address` → spec `gotosocial/smtp-email` (3 MUST: корректные env-ключи SMTP, непустой From, SPF/DMARC), archive.
- Коммит `1cee142`: docker-compose.yml + STATUS.md + openspec.
## 2026-09-19 — Управление проектом + SPF/DKIM + мониторинг
### Жёсткое правило openspec (AGENTS.md)
- По требованию пользователя в проект добавлено правило: **все изменения — через OpenSpec** (propose → apply-change → archive); исключение — явная команда владельца с `bypass-openspec:` в коммите.
- Носители: AGENTS.md (для агентов), openspec/config.yaml (context — инжектится во все артефакты), README.md (для людей), spec `gotosocial/project-governance` (3 требования со Scenario).
- change `enforce-openspec-workflow` → archive. Коммит `568335a`.
### SPF/DKIM (Jino, панель домена)
- SPF: `v=spf1 redirect=_spf.jino.ru` (хост dedinit.ru, IN TXT) — включено пользователем 19.09.
- DKIM: `v=DKIM1; t=s; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A…` (хост dkim._domainkey.dedinit.ru, IN TXT) — включено.
- DMARC: **не подтверждён** (ранее рекомендовано `v=DMARC1; p=none; rua=mailto:admin@dedinit.ru`). Открытая задача.
### Мониторинг :9464 (дашборд Grafana)
- Job `gotosocial` в /opt/monitoring/prometheus.yml уже был (127.0.0.1:9464/metrics, relabel instance=bigbox:9464); VictoriaMetrics (127.0.0.1:9090) тянет — проверено `/api/v1/series?match[]=gotosocial_instance_total_users`.
- Создан дашборд `/opt/monitoring/grafana/dashboards/gotosocial/gotosocial.json` (генератор `scripts/gen_gotosocial_dash.py`, 20 панелей: Доступность/Инстанс/Воркеры/HTTP/SQL/Процесс; uid gotosocial-main).
- dashboards.yml: добавлен провайдер `gotosocial-dashboards` (folder gotosocial). **Заодно починен дефект:** провайдер `garage-dashboards` смотрел на ВЕСЬ `/var/lib/grafana/dashboards`, дублируя nodes/vinogorod/vesti → Grafana блокировала запись всех новых дашбордов («the same UID is used more than once» + «no database write permissions because of duplicates»). Сужен до `/var/lib/grafana/dashboards/garage-cluster.json`; `docker compose restart grafana`.
- Проверка: `gotosocial-main` в grafana.db, папка gotosocial (id 23), dashboard_provisioning содержит запись.
- Openspec: change `add-gotosocial-dashboard` → spec `gotosocial-monitoring` (в /opt/monitoring), archive; коммит `ee92496` (monitoring); STATUS.md gotosocial → `275385c`.
### Git (эта сессия)
- /opt/gotosocial: `568335a` (governance), `275385c` (status) — запушены на gitverse.
- /opt/monitoring: `ee92496` — запушен на gitverse + mirror-sync на gitea (HTTP 200, POST /api/v1/repos/estorozhenko/monitoring/mirror-sync, токен из git-tokens.env).
- mirror-sync gitea: http://gitea.nixg.ru → 308 на https:// — использовать https или python urllib с redirect.
### Находка (не исправлена)
- config.py:45 — дефект `==` вместо `!=` (исходники GtS, /tmp/gts-src) — зафиксировать, не фикс.