feat(registration): open signups, 10/day limit; openspec: fix-apex-webfinger-and-registration

- docker-compose.yml: GTS_ACCOUNTS_REGISTRATION_OPEN=true, GTS_ACCOUNTS_REGISTRATION_DAILY_LIMIT=10
- openspec: proposal/specs/design/tasks for dynamic apex webfinger + registration + SMTP verify
- verified: /api/v1/instance → registrations:true; apex webfinger → acct:kpa39l@dedinit.ru; SMTP test OK
This commit is contained in:
estorozhenko
2026-09-07 05:48:46 +00:00
parent aa1814a9eb
commit cd7e279bb6
6 changed files with 227 additions and 0 deletions
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-09-07
@@ -0,0 +1,51 @@
## Goals
1. Восстановить короткий адрес `@kpa39l@dedinit.ru` в клиентах (Tusky/Mastodon).
2. Открыть регистрацию с лимитом 10 пользователей/день.
3. Убедиться, что SMTP работает (для писем подтверждения).
## Non-Goals
- Не меняем DNS (apex остаётся на Jino).
- Не меняем split-domain схему (host=social.dedinit.ru, account-domain=dedinit.ru).
- Не вводим PHP/мод_rewrite (недоступны на Jino).
- Не открываем регистрацию без лимита.
## Architecture
### Топология
```
Клиент (Tusky/Mastodon)
│ резолв @kpa39l@dedinit.ru
▼
dedinit.ru (apex, Jino/Apache)
│ /.well-known/webfinger?resource=acct:kpa39l@dedinit.ru
▼ RedirectMatch 301 (mod_alias, .htaccess)
social.dedinit.ru (GtS, vps02→WG→bigbox→docker)
│ /.well-known/webfinger?resource=...
▼ динамический JSON: subject=acct:kpa39l@dedinit.ru
```
### Регистрация
`docker-compose.yml` env:
- `GTS_ACCOUNTS_REGISTRATION_OPEN=true`
- `GTS_ACCOUNTS_REGISTRATION_DAILY_LIMIT=10`
approval_required=true (GtS по умолчанию): админ одобряет заявки.
Очередь одобрения: дефолт GtS (20) — при 10/день хватает.
## Risks / Open Questions
- **Tusky может кэшировать старый адрес**: после фикса apex-WebFinger нужно
перезайти/переподписаться в клиенте (или переустановить) — клиентский кэш
сам не сбросится. Это документируется, но не решается серверно.
- **`.htaccess` и rsync --delete**: деплой hugo (make deploy) использует
`rsync --delete public/`. `.htaccess` в `static/.well-known/` копируется в
`public/` и не затирается (файлы в public/ соответствуют static/, но
--delete удаляет только лишнее). Проверено: .htaccess в static/ →
public/.well-known/webfinger/.htaccess.
- Jino: `RedirectMatch` (mod_alias) работает; **mod_rewrite не работает** —
НЕ использовать RewriteRule.
- Статический index.html в webfinger УДАЛЁН — не восстанавливать.
@@ -0,0 +1,80 @@
## Why
Пользователь обнаружил, что в клиентах Tusky и Mastodon адрес его аккаунта
отображается как `@kpa39l@social.dedinit.ru` (длинный), хотя ожидался короткий
`@kpa39l@dedinit.ru`. Дополнительно: в феде виден только свой пост, и нужно
открыть регистрацию на инстансе (до 10 пользователей в день).
## Investigation
Разбор показал:
1. **Корень проблемы с адресом** — статический WebFinger на apex-домене:
- Инстанс (GtS) настроен split-domain: host=`social.dedinit.ru`,
account-domain=`dedinit.ru`. Сам GtS отдаёт корректный webfinger:
`https://social.dedinit.ru/.well-known/webfinger?resource=acct:kpa39l@dedinit.ru`
→ `subject: acct:kpa39l@dedinit.ru` (короткий).
- Но клиенты (Tusky/Mastodon) при резолве `@kpa39l@dedinit.ru` ходят на
**apex** `dedinit.ru/.well-known/webfinger`, где лежал **статический**
`index.html` с захардкоженным JSON про `estorozhenko` (игнорировал
`resource`). Клиент получал ответ про другого пользователя → показывал
фолбэк-адрес на основе host (`social.dedinit.ru`).
2. **Регистрация** была закрыта: `/api/v1/instance` → `registrations: false`,
`approval_required: true`.
3. **SMTP** настроен (smtp.jino.ru:587, social@dedinit.ru), но не проверялся.
## Solution
### 1. Динамический WebFinger на apex (Jino)
Apex обслуживается на Jino (Apache, без PHP/SSH-exec). Проверено: **mod_alias
`RedirectMatch` работает**. В `.htaccess` (webroot `/.well-known/webfinger/`)
добавлен редирект:
```
RedirectMatch 301 ^/\.well-known/webfinger(.*)$ https://social.dedinit.ru/.well-known/webfinger$1
```
Теперь `dedinit.ru/.well-known/webfinger?resource=...` отдаёт 301 на GtS,
который динамически резолвит ЛЮБОЙ аккаунт (включая будущих пользователей) —
короткий адрес `@user@dedinit.ru` восстанавливается для всех клиентов.
Аналогичный редирект добавлен для `/.well-known/nodeinfo`.
`host-meta` на apex уже указывал на template `social.dedinit.ru/.well-known/webfinger`
— оставлен.
### 2. Регистрация (10 пользователей/день)
В `docker compose.yml` (env GTS_*):
- `GTS_ACCOUNTS_REGISTRATION_OPEN: "true"`
- `GTS_ACCOUNTS_REGISTRATION_DAILY_LIMIT: "10"`
- `GTS_ACCOUNTS_REGISTRATION_BACKLOG_LIMIT`: не задан → дефолт 20 (очередь
одобрения). При желании можно поставить явно.
`approval_required` остаётся `true` (GtS по умолчанию требует одобрение админом
новых аккаунтов; при лимите 10/день очередь управляема).
### 3. Проверка SMTP
SMTP-конфиг проверен тестовым письмом: smtp.jino.ru:587 STARTTLS,
auth social@dedinit.ru → письмо успешно отправлено на kpa39l@yandex.ru.
SMTP работает, проблем нет.
## Files Changed
- `docker-compose.yml` (gotosocial): +2 env registration
- `static/.well-known/webfinger/.htaccess` (dedinit.ru): RedirectMatch 301
- `static/.well-known/nodeinfo/.htaccess` (dedinit.ru): RedirectMatch 301
- Jino webroot: удалён статический `index.html` из `/.well-known/webfinger/`
(заменён .htaccess-редиректом)
## Verification
- `curl -L 'https://dedinit.ru/.well-known/webfinger?resource=acct:kpa39l@dedinit.ru'`
→ `subject: acct:kpa39l@dedinit.ru` (короткий)
- `curl -L 'https://dedinit.ru/.well-known/webfinger?resource=acct:estorozhenko@dedinit.ru'`
→ `subject: acct:estorozhenko@dedinit.ru`
- `curl 'https://social.dedinit.ru/api/v1/instance'` → `registrations: true`
- SMTP-тест: OK
@@ -0,0 +1,52 @@
## Why
Клиенты отображают длинный адрес `@user@social.dedinit.ru` вместо короткого
`@user@dedinit.ru`, потому что apex-WebFinger был статическим. Нужно открыть
регистрацию с суточным лимитом.
## ADDED Requirements
### Requirement: Apex WebFinger резолвит любой аккаунт
- **MUST**: `https://dedinit.ru/.well-known/webfinger?resource=acct:USER@dedinit.ru`
возвращает (после 301) JSON от GtS с `subject: acct:USER@dedinit.ru` для
любого существующего локального пользователя, а не статический ответ
с захардкоженным пользователем.
- **MUST**: редирект реализован через `mod_alias RedirectMatch` в `.htaccess`
(Apache на Jino; mod_rewrite/PHP недоступны).
- **SHOULD**: `/.well-known/nodeinfo` на apex также редиректит на GtS.
#### Scenario: Резолв короткого адреса клиентом
- **GIVEN** локальный пользователь `kpa39l` на инстансе social.dedinit.ru
(account-domain dedinit.ru)
- **WHEN** клиент запрашивает `dedinit.ru/.well-known/webfinger?resource=acct:kpa39l@dedinit.ru`
- **THEN** ответ (после 301) содержит `subject: acct:kpa39l@dedinit.ru`
и aliases/links на `social.dedinit.ru/users/kpa39l`
- **THEN** Tusky/Mastodon отображают короткий `@kpa39l@dedinit.ru`
### Requirement: Регистрация открыта с лимитом 10/день
- **MUST**: `GTS_ACCOUNTS_REGISTRATION_OPEN=true` — страница `/signup` доступна,
`/api/v1/instance` → `registrations: true`.
- **MUST**: `GTS_ACCOUNTS_REGISTRATION_DAILY_LIMIT=10` — максимум 10
зарегистрированных/одобренных аккаунтов за 24 часа.
- **SHOULD**: SMTP работает (письма подтверждения/уведомления доставляются).
#### Scenario: Регистрация нового пользователя
- **GIVEN** инстанс с открытой регистрацией
- **WHEN** пользователь открывает `https://social.dedinit.ru/signup` и заполняет форму
- **THEN** заявка создаётся, попадает в очередь одобрения (approval_required)
- **THEN** при превышении 10 заявок за 24ч регистрация временно закрывается
### Requirement: SMTP-доставка писем
- **MUST**: SMTP (smtp.jino.ru:587 STARTTLS, social@dedinit.ru) отправляет
тестовое письмо без ошибок.
#### Scenario: Отправка тестового письма
- **GIVEN** SMTP-конфиг в docker-compose.yml + пароль в .env
- **WHEN** отправляется письмо на kpa39l@yandex.ru
- **THEN** сервер SMTP принимает сообщение (250), письмо доставляется
@@ -0,0 +1,39 @@
## Context
- Инстанс: social.dedinit.ru (GtS 0.22.1, docker на bigbox), account-domain dedinit.ru.
- Apex: dedinit.ru (Jino/Apache, доступ SFTP kpa39l.myjino.ru:2222).
- GtS конфигурируется ТОЛЬКО через GTS_* env (config.yaml не подхватывается).
## Tasks
### 1. Apex WebFinger → динамический
- [x] 1.1 Создать `static/.well-known/webfinger/.htaccess` в репо dedinit.ru:
`RedirectMatch 301 ^/\.well-known/webfinger(.*)$ https://social.dedinit.ru/.well-known/webfinger$1`
- [x] 1.2 Залить .htaccess на Jino (`/dedinit.ru/.well-known/webfinger/.htaccess`) через SFTP
- [x] 1.3 Удалить статический `index.html` из `/dedinit.ru/.well-known/webfinger/`
- [x] 1.4 Проверить: `curl -L 'https://dedinit.ru/.well-known/webfinger?resource=acct:kpa39l@dedinit.ru'`
→ `subject: acct:kpa39l@dedinit.ru`
### 2. Nodeinfo apex → динамический
- [x] 2.1 Создать `static/.well-known/nodeinfo/.htaccess` (RedirectMatch 301 на social)
- [x] 2.2 Залить на Jino, проверить `curl -L 'https://dedinit.ru/.well-known/nodeinfo'`
### 3. Регистрация (10/день)
- [x] 3.1 В `docker-compose.yml`: `GTS_ACCOUNTS_REGISTRATION_OPEN=true`,
`GTS_ACCOUNTS_REGISTRATION_DAILY_LIMIT=10`
- [x] 3.2 `docker compose up -d gotosocial` (recreate)
- [x] 3.3 Проверить `/api/v1/instance` → `registrations: true`; env в контейнере
### 4. SMTP
- [x] 4.1 Тестовое письмо через smtplib (smtp.jino.ru:587 STARTTLS, social@dedinit.ru)
на kpa39l@yandex.ru — доставлено
### 5. Финализация OpenSpec
- [x] 5.1 Валидация change: `openspec validate fix-apex-webfinger-and-registration`
- [ ] 5.2 Коммит + пуш docker-compose.yml (gotosocial) и .htaccess (dedinit.ru)
- [ ] 5.3 Архив change (`openspec archive`)