Один бинарь на stdlib, ноль зависимостей: подкоманды serve, vapid, version; статика из embed с ETag и Cache-Control: no-cache; /healthz; заголовки безопасности ADR-021 на всех ответах, включая ошибки; лимит тела 32 КиБ. Путь в логе — в percent-форме: подделать строку журнала запросом нельзя. Ни IP, ни ника, ни query в логах нет. Клиент — знак «Скобы» и слово bare: без script-тегов, inline-стилей и внешних ресурсов. ADR-025: go:embed не выходит за каталог пакета, поэтому объявление статики живёт в корне модуля, а клиент остаётся в web/. ADR-026: 413 отдаётся кодом too_large, неподдерживаемый метод — 404; оба добавлены в перечень protocol.md. Сервер: пользователь bare, /opt/bare, /var/lib/bare, /etc/bare/env 0600, systemd-юнит, nginx с сертификатом certbot (плагин nginx, как у соседей). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015DbCjVfTFq4ZFG8juD45YJ
9.7 KiB
План реализации v1
Документ для исполнителя — человека или агента. Всё, что здесь, выводится из ADR и спецификаций; при расхождении правы ADR. Этапы идут по порядку, каждый заканчивается работающим деплоем на bare.xmatic.team и коммитом.
Источники истины
| вопрос | документ |
|---|---|
| что и почему | philosophy.md, architecture.md, threat-model.md, decisions/ |
| криптография, байт в байт | crypto.md |
| HTTP-API, SSE, коды ошибок | protocol.md |
схема SQLite, IndexedDB, формат .bare |
storage.md |
| экраны, тексты, поведение | ui.md, identity/ |
| сервер, nginx, systemd | deploy.md |
Раскладка репозитория
embed.go //go:embed web в корне модуля (ADR-025)
cmd/bare/main.go подкоманды: serve, vapid, version
internal/config/ переменные BARE_*
internal/store/ SQLite, migrations/*.sql (embed), запросы
internal/auth/ argon2id, сессии, cookie
internal/hub/ SSE-соединения по deviceId
internal/push/ webpush-go, правила ADR-023
internal/api/ маршруты, валидация, лимиты, заголовки
internal/web/ embed web/, отдача статики
web/
index.html app.css manifest.json sw.js
icons/ уже в репозитории
js/main.js загрузка, роутинг, состояние
js/api.js fetch-обёртки, SSE, ACK
js/crypto.js всё из crypto.md
js/db.js IndexedDB из storage.md
js/ulid.js ULID
js/ui/*.js экраны из ui.md
js/export.js .bare
scripts/deploy.sh
Go — последняя стабильная версия, маршрутизация net/http с шаблонами методов ("POST /api/messages"). Прямые зависимости ровно три (ADR-020). Клиент — ES-модули, без сборки, без inline-стилей и скриптов, innerHTML запрещён.
Этап 0 — скелет и деплой
go mod init,cmd/bare,serveслушаетBARE_ADDR, отдаётweb/изembed,/healthz, заголовки безопасности.web/index.html— страница со знаком и словомbare,app.cssс переменными изidentity/brief.md,manifest.json, пустойsw.jsс версией.scripts/deploy.sh; на сервере — пользователь, каталоги,env, юнит, nginx, сертификат поdeploy.md.
Готово, когда https://bare.xmatic.team/ открывается с правильным CSP, /healthz отвечает ok, journalctl -u bare чист.
Этап 1 — аккаунты
- Миграция 001,
storeсuser_version, фоновая чистка. auth: argon2id с параметрами ADR-021, сессии, cookie, проверкаOrigin.- Эндпоинты:
config,kdf,register,login,logout,me,password,DELETE /api/me,users/{nick}. - Клиент:
crypto.js(мастер, authKey, kek, блоб, ключевая пара, отпечаток), экран входа и регистрации, сохранениеCryptoKeyв IndexedDB, автоповышение итераций, настройки с «сменить пароль» и «выйти». - Тесты Go: миграции на пустой базе, регистрация и вход, неверный
authKey, смена пароля сlogoutOthers.
Готово, когда регистрация и вход работают на телефоне и десктопе, вход на втором устройстве расшифровывает тот же ключ (отпечатки совпадают), пароль в сетевых запросах не встречается.
Этап 2 — чат 1:1
devices,hub,queue,POST /api/messages,ack,eventsс воспроизведением очереди и пингом.- Клиент:
ulid.js,db.js,api.jsс SSE и ACK после записи, шифрование сообщений, экран чата (десктоп и мобильный по эталону), список чатов, «новый чат», разделители дат и «новые», pending/failed, повтор после реконнекта. - Контакты:
GET/POST/DELETE /api/contacts, автосоздание при первом сообщении. - Тесты Go: фан-аут по устройствам без эха отправителю, ACK удаляет, повтор очереди при реконнекте,
clock_skew, лимит 30/мин.
Готово, когда два аккаунта переписываются в реальном времени, второе устройство получателя получает копию, офлайн-устройство получает очередь при открытии, в базе — только шифротекст.
Этап 3 — ключи и комнаты
- TOFU: хранилище
peers, карточка контакта, предупреждение о смене ключа, «доверять новому ключу», повторная расшифровкаraw. - Комнаты:
roomsиroom_keys, все эндпоинты изprotocol.md, событияroom/room_left, передача владения, rekey при выходе. - Клиент: создание комнаты, участники, заворачивание и разворачивание ключей, хранение
roomKeys, отправка с текущимkeyId, расшифровка любым известным. - Тесты Go:
keys_mismatch,key_exists, выход владельца, удаление пустой комнаты, обрезка ключей до двух.
Готово, когда трое переписываются в комнате, добавленный четвёртый читает только новое, вышедший не получает новых сообщений после rekey, подмена public_key в базе вручную вызывает предупреждение у собеседника.
Этап 4 — PWA и пуши
sw.js: кэш оболочки,push,notificationclick;manifest.jsonс иконками;apple-touch-icon.PUT/DELETE /api/devices/{id}/push, отправка по правилам ADR-023, обработка 404/410.- Клиент: запрос разрешения после первого сообщения, настройки уведомлений, баннер установки на iOS,
beforeinstallprompt.
Готово, когда закрытое PWA на iPhone и Android получает пуш и открывается на нужном чате; повторные сообщения до открытия пуш не порождают.
Этап 5 — история
- Экспорт и импорт
.bareпоcrypto.mdиstorage.md; идемпотентность; «архив создан другим аккаунтом». - Настройки: устройства (список, удаление), занятое место, удаление аккаунта.
- Пагинация ленты по 50 с подгрузкой вверх.
Готово, когда экспорт с одного устройства и импорт на другом дают одинаковую ленту без дублей, повторный импорт ничего не меняет, чужой архив отклоняется до расшифровки.
Этап 6 — закалка
- Rate limiting по всем правилам ADR-021,
413,429сRetry-After. - Проверка CSP в консоли браузера: ноль нарушений.
- Прогон модели угроз по коду: пароль не уходит,
fromставит сервер,Originпроверяется, cookie с нужными флагами. - Синхронизация документов с кодом: расхождение — правка документа через ADR или правка кода.
Определение готовности v1
Все шесть этапов; тесты Go зелёные; ручной прогон сценариев из каждого «готово, когда» на iOS Safari (PWA), Android Chrome, десктопных Chrome, Firefox, Safari; README.md обновлён со статуса «проектирование».
Правила для исполнителя
- Сомнение в спецификации — сначала ADR, потом код. Не дописывать спецификацию молча.
- Новая зависимость, новый эндпоинт, новое поле в конверте — только через ADR.
- Каждый этап — отдельный коммит или серия коммитов с деплоем; не копить.
- Не добавлять фич сверх
ui.md: ни тем, ни аватаров, ни «печатает», ни статусов прочтения.