Sprint 0 / #16: каркас монорепозитория, ADR-001..008 и линтеры #47
@@ -0,0 +1,21 @@
|
|||||||
|
# NNN. Название решения
|
||||||
|
|
||||||
|
- **Статус:** предложено | принято | устарело
|
||||||
|
- **Дата:** YYYY-MM-DD
|
||||||
|
- **Контекст/issue:** #N
|
||||||
|
|
||||||
|
## Контекст
|
||||||
|
|
||||||
|
Что вынуждает принять решение и какие есть ограничения (хостовое окружение, привилегии, один шлюз, целевой UX).
|
||||||
|
|
||||||
|
## Решение
|
||||||
|
|
||||||
|
Что именно решили, одним абзацем.
|
||||||
|
|
||||||
|
## Альтернативы
|
||||||
|
|
||||||
|
Что рассмотрели и почему отклонили.
|
||||||
|
|
||||||
|
## Последствия
|
||||||
|
|
||||||
|
Положительные и отрицательные следствия; что решение накладывает на задачи эпика.
|
||||||
@@ -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 нет.
|
||||||
@@ -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 не разрывает туннель.
|
||||||
@@ -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, для которого решение принято.
|
||||||
Reference in New Issue
Block a user