From 63e653b710e2bddd1f20ca59fdac17a17669dd4c Mon Sep 17 00:00:00 2001 From: embeddevio Date: Wed, 21 Oct 2026 23:29:18 +0300 Subject: [PATCH] =?UTF-8?q?docs(#16):=20ADR-001,=20ADR-002,=20=D0=B8=D0=BD?= =?UTF-8?q?=D0=B4=D0=B5=D0=BA=D1=81=20=D0=B8=20=D1=88=D0=B0=D0=B1=D0=BB?= =?UTF-8?q?=D0=BE=D0=BD=20ADR?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/adr/000-template.md | 21 ++++++++++++ docs/adr/001-backend-python-fastapi.md | 27 ++++++++++++++++ docs/adr/002-single-image-modular-monolith.md | 32 +++++++++++++++++++ docs/adr/README.md | 16 ++++++++++ 4 files changed, 96 insertions(+) create mode 100644 docs/adr/000-template.md create mode 100644 docs/adr/001-backend-python-fastapi.md create mode 100644 docs/adr/002-single-image-modular-monolith.md create mode 100644 docs/adr/README.md diff --git a/docs/adr/000-template.md b/docs/adr/000-template.md new file mode 100644 index 0000000..328cca6 --- /dev/null +++ b/docs/adr/000-template.md @@ -0,0 +1,21 @@ +# NNN. Название решения + +- **Статус:** предложено | принято | устарело +- **Дата:** YYYY-MM-DD +- **Контекст/issue:** #N + +## Контекст + +Что вынуждает принять решение и какие есть ограничения (хостовое окружение, привилегии, один шлюз, целевой UX). + +## Решение + +Что именно решили, одним абзацем. + +## Альтернативы + +Что рассмотрели и почему отклонили. + +## Последствия + +Положительные и отрицательные следствия; что решение накладывает на задачи эпика. diff --git a/docs/adr/001-backend-python-fastapi.md b/docs/adr/001-backend-python-fastapi.md new file mode 100644 index 0000000..9a96158 --- /dev/null +++ b/docs/adr/001-backend-python-fastapi.md @@ -0,0 +1,27 @@ +# 001. Backend — Python 3.12 + FastAPI + +- **Статус:** принято +- **Контекст/issue:** #15, #31 + +## Контекст + +Нужен control plane: REST API, WebSocket, планировщик, парсинг конфигов, работа с SQLite. +Низкоуровневая работа сети и шифрования выполняется не нашим кодом, а бинарями `awg` и `amneziawg-go`, +трафик маршрутизирует ядро. + +## Решение + +Control plane — **Python 3.12 + FastAPI (async)**. Go остаётся только там, где он уже есть: +`amneziawg-go` и `amneziawg-tools` собираются в multi-stage Dockerfile (ADR-002). + +## Альтернативы + +- **Go целиком** — быстрее на сетевом I/O, но маршрутизацией занимается ядро, а не процесс; + выигрыш не оправдывает рост стоимости контракта с UI (схемы, генерация клиента, миграции, веб-фреймворк). +- **Node.js** — преимуществ перед Python на нагрузке в единичного пользователя нет. + +## Последствия + +- OpenAPI-first: FastAPI отдаёт схему, что прямо соответствует задаче #19. +- В образе появляется Python runtime (плюс 40–60 MB к размеру). +- Дисциплина async: внешние команды — только `asyncio.create_subprocess_exec`, блокирующего кода в event loop нет. diff --git a/docs/adr/002-single-image-modular-monolith.md b/docs/adr/002-single-image-modular-monolith.md new file mode 100644 index 0000000..765dc1a --- /dev/null +++ b/docs/adr/002-single-image-modular-monolith.md @@ -0,0 +1,32 @@ +# 002. Модульный монолит в одном образе, два процесса + +- **Статус:** принято +- **Контекст/issue:** #15, #18 + +## Контекст + +ТЗ требует единый Docker-образ с минимальными параметрами запуска. Часть операций требует root +(интерфейс awg, iptables/ipset, dnsmasq на порту 53), а веб-часть должна быть скомпрометирована безопасно — без root. + +## Решение + +Один образ, два процесса под супервизором `deploy/entrypoint.sh`: + +| Процесс | Права | Ответственность | +|---|---|---| +| `awg-agent` | root, NET_ADMIN | туннель, netfilter, routing, dnsmasq, метрики | +| `awg-api` | непривилегированный | FastAPI, REST/WS, статика UI | + +Обмен — unix-socket `/run/awg/agent.sock`, общее состояние — SQLite в `/data` и конфиги в `/config`. + +## Альтернативы + +- **Микросервисы** (отдельные контейнеры agent/api/dns) — общая сеть, координация и дублирование + привилегий; выгода масштабирования на одном шлюзе отсутствует. +- **Один процесс под root** — проще, но даёт веб-слою полные права; неприемлемо. + +## Последствия + +- Супервизор обязан пробрасывать сигналы и коды выхода, иначе Docker не заметит падения процесса. +- Права на unix-socket входят в security-проверки (#44). +- Перезапуск API не разрывает туннель. diff --git a/docs/adr/README.md b/docs/adr/README.md new file mode 100644 index 0000000..9cf9ed5 --- /dev/null +++ b/docs/adr/README.md @@ -0,0 +1,16 @@ +# ADR — архитектурные решения awg-dash + +Один файл = одно решение. Формат: контекст -> решение -> последствия. + +| ADR | Решение | +|---|---| +| [001](001-backend-python-fastapi.md) | Backend — Python 3.12 + FastAPI | +| [002](002-single-image-modular-monolith.md) | Модульный монолит в одном образе, 2 процесса | +| [003](003-reconcile-desired-state.md) | Декларативный reconcile вместо императивных скриптов | +| [004](004-geoip-ripe-provider.md) | GeoIP: RIPE Stat + локальный кэш + абстракция провайдера | +| [005](005-dns-dnsmasq.md) | DNS: встроенный dnsmasq + перехват порта 53 | +| [006](006-state-sqlite.md) | Состояние: SQLite (WAL) + /config + /data | +| [007](007-embedded-web-server.md) | Web-сервер: встроенный uvicorn, без nginx | +| [008](008-api-ui-security.md) | Безопасность API/UI: токен, CSRF, rate limit, маскировка секретов | + +Новые решения добавляются со следующим номером и ссылкой на issue, для которого решение принято.