- 7 admin per-task files moved from .tasks-imported/ to .tasks/ (renames) - NEXT-SESSION-PROMPT.md (iis-migration session handoff) kept as sibling - .tasks-imported/STATUS.md removed (content merged into admin STATUS.md) - admin STATUS.md gains 'Imported from MoreThenCms' section with all 7 admin done tasks verbatim. 4 CMS-domain task blocks excluded (stay in MoreThenCms): cms-admin-assets-root-folders-seed, cms-port-leak-fix, traefik-maljarka-502-bug, cms-maljarka-https-mode-bug-fix - morecms-subtree-split marked done (acceptance: 4 split branches exist with per-file history) - admin-subtree-import-and-cleanup marked active (in-progress this session) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
100 lines
6.6 KiB
Markdown
100 lines
6.6 KiB
Markdown
# vds-ntfy-push
|
||
|
||
## Goal
|
||
|
||
Self-host [ntfy.sh](https://ntfy.sh) на VDS для push-нотификаций на телефон. Docker-image `binwiederhier/ntfy`. Использовать для:
|
||
- [[vds-backup-rsync-kreknin]] backup status (вместо/параллельно email на vitya.kuznetsov@gmail.com)
|
||
- Monitoring alerts (CMS down, traefik certs истекают, disk fill, registry GC fail, etc.)
|
||
- Любые ad-hoc уведомления от ops-скриптов
|
||
|
||
Domain: `ntfy.vds.kzntsv.site` (под уже существующим wildcard `*.vds.kzntsv.site`).
|
||
|
||
## Open questions
|
||
|
||
- [x] ~~**Auth model:**~~ per-topic ACL + admin user `vitya` (resolved 2026-05-20). Реализация: `NTFY_AUTH_DEFAULT_ACCESS=deny-all` + `ntfy user add --role=admin vitya`. Admin role → rw to all topics, per-topic ACL не нужен. Anon (no creds) → 403 на publish и subscribe ✅.
|
||
- [x] ~~**Топики:**~~ начали с двух: `vds-backup` (для vds-backup-rsync-kreknin) + `vds-ops` (general alerts: cert expiry, disk fill, container crashes). Можно добавлять без миграции — admin role даёт доступ ко всему автоматом.
|
||
- [x] ~~**Phone app:**~~ Android (resolved 2026-05-20) → ntfy Android app, pure self-host без APNs middleware.
|
||
- [x] ~~**Persistence:**~~ sqlite в `/opt/stacks/ntfy/data/` (cache.db + auth.db). Volume mount `./data:/var/cache/ntfy`.
|
||
- [x] ~~**Phone-side smoke:**~~ confirmed 2026-05-20 22:54 MSK — оба push'а («Topic renamed», «Password updated») пришли в Android ntfy app, screenshot в conversation.
|
||
|
||
## Implementation sketch
|
||
|
||
`/opt/stacks/ntfy/docker-compose.yml`:
|
||
|
||
```yaml
|
||
services:
|
||
ntfy:
|
||
image: binwiederhier/ntfy
|
||
container_name: ntfy
|
||
restart: unless-stopped
|
||
command:
|
||
- serve
|
||
networks:
|
||
- proxy
|
||
volumes:
|
||
- ./data:/var/cache/ntfy
|
||
- ./etc:/etc/ntfy
|
||
environment:
|
||
NTFY_BASE_URL: https://ntfy.vds.kzntsv.site
|
||
NTFY_CACHE_FILE: /var/cache/ntfy/cache.db
|
||
NTFY_AUTH_FILE: /var/cache/ntfy/auth.db
|
||
NTFY_AUTH_DEFAULT_ACCESS: deny-all
|
||
NTFY_BEHIND_PROXY: true
|
||
NTFY_ATTACHMENT_CACHE_DIR: /var/cache/ntfy/attachments
|
||
labels:
|
||
- traefik.enable=true
|
||
- traefik.http.routers.ntfy.rule=Host(`ntfy.vds.kzntsv.site`)
|
||
- traefik.http.routers.ntfy.entrypoints=websecure
|
||
- traefik.http.routers.ntfy.tls.certresolver=letsEncrypt
|
||
- traefik.http.services.ntfy.loadbalancer.server.port=80
|
||
|
||
networks:
|
||
proxy:
|
||
external: true
|
||
```
|
||
|
||
После up — `docker exec ntfy ntfy user add --role=admin vitya` → задать пароль → грант access на топики.
|
||
|
||
Publish from any script:
|
||
```bash
|
||
curl -u "vitya:$NTFY_PASS" \
|
||
-H "Title: VDS backup completed" \
|
||
-H "Tags: white_check_mark" \
|
||
-H "Priority: default" \
|
||
-d "Size 12.5G, 18m12s, kreknin OK" \
|
||
https://ntfy.vds.kzntsv.site/vds-backup
|
||
```
|
||
|
||
Phone subscribes to `https://ntfy.vds.kzntsv.site/vds-backup` (с basic auth) — получает уведомление.
|
||
|
||
## Decisions log
|
||
|
||
- **2026-05-20** — Image pinned `binwiederhier/ntfy:v2.11.0` (conservative, matches pin policy остальных стэков: traefik v2.11, portainer 2.21.5).
|
||
- **2026-05-20** — Auth: `NTFY_AUTH_DEFAULT_ACCESS=deny-all` + admin user `vitya` (random hex16 password). Admin role гранит rw ко всем топикам автоматом — per-topic `ntfy access` calls returned `user vitya is an admin user, access control entries have no effect`. Не нужны явные grants; разлогиниться от admin позже если будет потребность в ограниченных read-only users.
|
||
- **2026-05-20** — Topics: `vds-backup` + `vds-ops` (минимум для текущих нужд). Расширение бесплатное (admin → rw to all).
|
||
- **2026-05-20** — TLS через traefik LE http-01 (issuer R12, valid Aug 18 2026); ntfy listens HTTP `:80` внутри `proxy` network, `NTFY_BEHIND_PROXY=true` чтобы trust X-Forwarded-* от traefik.
|
||
- **2026-05-20** — Smoke green: anon publish/subscribe → 403, authed publish vds-backup+vds-ops → 200 с JSON id/ts.
|
||
- **2026-05-20** — User feedback: random hex password неудобен для ввода в phone app. Меняю на `Pryakhin9` (consistency с TRAEFIK_DASHBOARD_PASS). Trade-off: меньше entropy, но scope ограничен (admin role, single user, push-сервис без чувствительных данных, can rotate если threat model изменится). Lesson — для human-input creds (phone/web UI) использовать память user'а, не random hex; random hex оставить для script-only (DB, API keys).
|
||
- **2026-05-20** — Topics renamed `backup` → `vds-backup`, `ops` → `vds-ops` (consistency с VDS scope, упрощает filtering если позже добавятся nas-* или kreknin-* топики).
|
||
- **2026-05-20** — Phone-verified: Android ntfy app subscription → оба push'а пришли в течение секунд. End-to-end complete.
|
||
|
||
## Completed steps
|
||
|
||
- [x] `/opt/stacks/ntfy/{data}` создано (owner vitya:vitya)
|
||
- [x] `docker-compose.yml` написан с traefik labels (Host, websecure, letsEncrypt, port 80)
|
||
- [x] `docker compose up -d` → container healthy, listening :80
|
||
- [x] Admin user `vitya` добавлен через `printf "%s\n%s\n" "$PASS" "$PASS" | docker exec -i ntfy ntfy user add --role=admin vitya`
|
||
- [x] LE cert issued via http-01, R12, valid 2026-05-20 → 2026-08-18
|
||
- [x] Smoke test (publish/subscribe/anon-deny) green
|
||
- [x] Creds saved: `/opt/stacks/ntfy/.env` (chmod 600) + Windows `~/projects/.common/secrets/vds-kzntsv.env`
|
||
- [x] Password rotated random-hex → `Pryakhin9` per user request (ntfy user change-pass)
|
||
- [x] Topics renamed `backup`/`ops` → `vds-backup`/`vds-ops`
|
||
- [x] Phone-verified (Android ntfy app, screenshot 22:54 MSK)
|
||
|
||
## Notes
|
||
|
||
- Триггер: после `[[vds-kzntsv-bootstrap]]` Phase 3 и до или одновременно с `[[vds-backup-rsync-kreknin]]` (backup pipeline хочет ntfy для статус-уведомлений).
|
||
- Интегрируется с `[[vds-backup-rsync-kreknin]]` — backup script публикует через ntfy + дублируется email'ом для надёжности.
|
||
- **Android setup hint:** ntfy app → Add subscription → Topic `vds-backup` (или `vds-ops`) → Server `https://ntfy.vds.kzntsv.site` → enable «Use auth» → user `vitya` / password из vds-kzntsv.env.
|
||
- Cert renewal — automatic via traefik (letsEncrypt resolver), нет дополнительных шагов.
|