Смержить production-структуру документации

# Conflicts:
#	VERSION.properties
#	shine-UI/js/pages/registration-payment-view.js
#	shine-UI/styles/components.css
This commit is contained in:
AidarKC
2026-07-20 11:30:00 +04:00
63 changed files with 1540 additions and 2277 deletions
+1 -1
View File
@@ -129,7 +129,7 @@
- `shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/JsonHandlerRegistry.java`.
Если операция зарегистрирована в `HANDLERS` и `REQUEST_TYPES`, она считается доступной через JSON/WebSocket API. Общий актуальный индекс таких операций поддерживается в `Dev_Docs/API/09_Operations_Index.md`.
Если операция зарегистрирована в `HANDLERS` и `REQUEST_TYPES`, она считается доступной через JSON/WebSocket API. Общий актуальный индекс таких операций поддерживается в `docs/API/09_Operations_Index.md`.
---
+2 -2
View File
@@ -38,7 +38,7 @@
Отдельно появился новый серверный сценарий pairing через доверенный homeserver/ESP. Он не заменяет обычный вход и описан в:
- `Dev_Docs/Протоколы/ESP_Pairing_и_режимы_подключения.md`
- `docs/Протоколы/ESP_Pairing_и_режимы_подключения.md`
Кратко:
@@ -321,7 +321,7 @@ SESSION_LOGIN:{sessionId}:{timeMs}:{nonce}
Точные форматы этих операций см. в `03_Session_Management_API.md` и в протокольном документе:
- `Dev_Docs/Протоколы/ESP_Pairing_и_режимы_подключения.md`
- `docs/Протоколы/ESP_Pairing_и_режимы_подключения.md`
---
@@ -4,8 +4,8 @@
Подробная логика DM и бинарного формата:
- `Dev_Docs/Personal_Messages/Протокол_DM_v1.md`
- `Dev_Docs/Personal_Messages/Формат_DM_v1.md`
- `docs/Personal_Messages/Протокол_DM_v1.md`
- `docs/Personal_Messages/Формат_DM_v1.md`
Важно:
@@ -22,4 +22,4 @@
## Примечание
Если нужно добавить новый тип или подтип блока, сначала обновляйте профильный файл этого раздела, затем API-документацию в `Dev_Docs/API`.
Если нужно добавить новый тип или подтип блока, сначала обновляйте профильный файл этого раздела, затем API-документацию в `docs/API`.
+7 -7
View File
@@ -52,7 +52,7 @@
- после рестарта сервер добивает `BlockchainTmpRecovery` и `BlockchainResyncRecovery`;
- `aidartest-001` успешно подтягивается с `shineup.me`;
- итоговое локальное состояние по `aidartest-001` дошло до `last_block_number=13`.
- В `Dev_Docs/Blockchain/sync-between-servers.md` добавлен практический результат ручной проверки на тестовом сервере.
- В `docs/Blockchain/sync-between-servers.md` добавлен практический результат ручной проверки на тестовом сервере.
## 2026-06-26 17:03:22 +0400
- Базовый коммит-ориентир: `71fdee0`.
@@ -60,21 +60,21 @@
- `BlockchainTmpRecoveryOnStartup` теперь разбирает marker-driven recovery для обычной записи блока:
- если marker есть, recovery либо завершает swap tmp -> main, либо удаляет мусор;
- если marker нет, временные артефакты считаются мусором и удаляются.
- В `Dev_Docs/Blockchain/sync-between-servers.md` добавлено описание обычного `AddBlock` recovery и разделение между `write_pending` и `resync_pending`.
- В `docs/Blockchain/sync-between-servers.md` добавлено описание обычного `AddBlock` recovery и разделение между `write_pending` и `resync_pending`.
## 2026-05-24 11:40:00 +0300
- Базовый коммит-ориентир: `abdce05`.
- `TEXT_REPOST (subType=30)` оставлен как зарезервированный формат, но новые блоки репоста временно отключены на уровне `AddBlock`.
- В `11_TEXT_Blocks.md` зафиксировано, что запись `TEXT_REPOST` временно не используется до будущей реализации.
- В `Dev_Docs/API/04_Add_Block_to_Blockchain_API.md` добавлен код отказа `repost_disabled`.
- В `docs/API/04_Add_Block_to_Blockchain_API.md` добавлен код отказа `repost_disabled`.
## 2026-05-21 19:05:00 +0300
- Базовый коммит-ориентир: `5344c42`.
- Добавлен новый TEXT-подтип `TEXT_REPOST (subType=30)`:
- обновлён перечень типов в `11_TEXT_Blocks.md`;
- обновлена быстрая карта типов в `00_Blockchain_Formats_and_Block_Types.md`.
- Уточнено API-описание поддержанных подтипов в `Dev_Docs/API/04_Add_Block_to_Blockchain_API.md`.
- В документе `Dev_Docs/API/08_MCP_Чтение_и_дозапись_персонального_публичного_чата.md` зафиксировано, что чтение канала учитывает `TEXT_POST` и `TEXT_REPOST`.
- Уточнено API-описание поддержанных подтипов в `docs/API/04_Add_Block_to_Blockchain_API.md`.
- В документе `docs/API/08_MCP_Чтение_и_дозапись_персонального_публичного_чата.md` зафиксировано, что чтение канала учитывает `TEXT_POST` и `TEXT_REPOST`.
## 2026-05-20 11:34:17 +0300
- Базовый коммит-ориентир: `a53444b`.
@@ -82,7 +82,7 @@
- `60/61``known_person / unknown_person` (знаю этого человека);
- `70/71``shine_confirmed / shine_unconfirmed` (точно уверен, что сияющий);
- `74/75``shine_seen / shine_unseen` (мало знаком, но видел сияющим).
- Обновлён список CONNECTION-подтипов в `Dev_Docs/API/04_Add_Block_to_Blockchain_API.md`.
- Обновлён список CONNECTION-подтипов в `docs/API/04_Add_Block_to_Blockchain_API.md`.
## 2026-05-19 20:30:21 +0300
- Базовый коммит-ориентир: `7986184`.
@@ -107,4 +107,4 @@
- Для персонального канала (`type=100`) включена сборка парного потока при чтении (`A->B` + `B->A`, если существует).
- Добавлена поддержка командного префикса `/.` и команды `/.desc` для актуализации описания канала при чтении.
- Зафиксированы команды `/.add` и `/.remove` для каналов `type=200` (зарезервировано под расширение участниками).
- В `AGENTS.md` добавлено обязательное правило актуализации документации в `Dev_Docs/Blockchain/`.
- В `AGENTS.md` добавлено обязательное правило актуализации документации в `docs/Blockchain/`.
+1 -1
View File
@@ -193,7 +193,7 @@ Full resync запускается только тогда, когда:
### 5.4 Разрешение конфликтов
- Блоки пользовательского блокчейна: порядок определяется глобальным номером блока.
Конфликтующие ветки (fork) разрешаются по правилам `AddBlock` (см. `Dev_Docs/Blockchain/README.md`).
Конфликтующие ветки (fork) разрешаются по правилам `AddBlock` (см. `docs/Blockchain/README.md`).
- DM: конфликтов нет, `message_key` уникален.
## 6. Маршрутизация DM между серверами
+1 -1
View File
@@ -197,7 +197,7 @@
- нужен реальный прогон на test2;
- затронута интеграция с Solana;
тогда нужно добавить файл в `Dev_Docs/Pending_Features/`.
тогда нужно добавить файл в `docs/Pending_Features/`.
## Что пока не оформлено для Miro
+3 -3
View File
@@ -6,7 +6,7 @@
> Если в коде меняется деривация (формула секрета, параметры Argon2id, соль, формула
> ключа, разделитель `|`, набор/имена суффиксов, формат homeserver-ключа, связь
> dev-ключ ↔ Solana-адрес) — **в том же изменении обязательно править этот документ**.
> Роли и назначение ключей описаны отдельно в `Dev_Docs/Keys/README.md` (архитектура).
> Роли и назначение ключей описаны отдельно в `docs/Keys/README.md` (архитектура).
> Здесь — только механика. Документ намеренно краткий.
---
@@ -59,7 +59,7 @@ seed(32) = SHA-256(material)
| device / **Solana** | `client.key` | Ключ устройства = Solana-ключ. Fee payer и подпись Solana-транзакций; адрес кошелька = `base58(clientPub)`. См. §3. |
| homeserver | `homeserver.key:<имя>` | Ключ homeserver-устройства, по одному на каждый homeserver (различитель — имя). См. §4. |
Полные роли каждого ключа — в `Dev_Docs/Keys/README.md`.
Полные роли каждого ключа — в `docs/Keys/README.md`.
---
@@ -77,7 +77,7 @@ seed(32) = SHA-256(material)
Кратко про роли на Solana: `root.key` — это **главный (master) ключ**: им управляют PDA-записью
(`create/update`) и через это можно заменить все остальные ключи; `client.key` — это **пополняемый
кошелёк и плательщик комиссий**. Полное описание ролей — `Dev_Docs/Keys/README.md`.
кошелёк и плательщик комиссий**. Полное описание ролей — `docs/Keys/README.md`.
---
+9 -9
View File
@@ -30,7 +30,7 @@
`root key` — это **главный (master) ключ** в следующем смысле: зная `root key`, можно управлять пользовательской PDA-записью в Solana (`create_user_pda` / `update_user_pda`) и тем самым **заменить все остальные ключи** пользователя (device, blockchain, homeserver). Поэтому компрометация `root key` равносильна компрометации всей личности пользователя.
Важно не путать авторитет и кошелёк: `root key` — это авторитет над PDA-записью, а **SOL-комиссии за create/update платит `client key`** (он же fee payer и адрес для пополнения). Подробнее о том, какой ключ за что отвечает на Solana, — в `Dev_Docs/Keys/DERIVATION.md`, §3.
Важно не путать авторитет и кошелёк: `root key` — это авторитет над PDA-записью, а **SOL-комиссии за create/update платит `client key`** (он же fee payer и адрес для пополнения). Подробнее о том, какой ключ за что отвечает на Solana, — в `docs/Keys/DERIVATION.md`, §3.
## `blockchain key`
@@ -65,7 +65,7 @@
Arweave-кошелёк должен выводиться из `client key` по протоколу:
- `Dev_Docs/Протоколы/SHINE_ARWEAVE_DERIVATION_V1.md`
- `docs/Протоколы/SHINE_ARWEAVE_DERIVATION_V1.md`
Если пользователь теряет только `client key`, в худшем случае ломается повседневная переписка и доступ конкретных устройств к ежедневным операциям. `root key` и `blockchain key` при правильной архитектуре остаются отдельно защищёнными.
@@ -158,13 +158,13 @@ Self-message - это сообщение пользователя самому
## Связанные документы
- `Dev_Docs/Keys/DERIVATION.md` - **источник истины по конкретной деривации** секрета и ключей (формулы Argon2id, `base64|suffix→SHA-256→Ed25519`, суффиксы `root.key`/`bch.key`/`client.key`/`homeserver.key:<имя>`, Solana-ключ, ссылки на код).
- `Dev_Docs/Personal_Messages/Протокол_DM_v1.md` - текущая логическая документация личных сообщений.
- `Dev_Docs/Personal_Messages/Формат_DM_v1.md` - точный байтовый формат личных сообщений.
- `Dev_Docs/Blockchain/README.md` - точка входа по форматам SHiNE-блокчейна.
- `Dev_Docs/Solana_Architecture/README.md` - архитектура Solana-программ, PDA-счетов, DAO и движения средств.
- `Dev_Docs/Инициализация_Solana_регистрации/README.md` - деплой и первичная инициализация Solana-регистрации.
- `Dev_Docs/Протоколы/SHINE_ARWEAVE_DERIVATION_V1.md` - derivation Arweave-кошелька из `client key`.
- `docs/Keys/DERIVATION.md` - **источник истины по конкретной деривации** секрета и ключей (формулы Argon2id, `base64|suffix→SHA-256→Ed25519`, суффиксы `root.key`/`bch.key`/`client.key`/`homeserver.key:<имя>`, Solana-ключ, ссылки на код).
- `docs/Personal_Messages/Протокол_DM_v1.md` - текущая логическая документация личных сообщений.
- `docs/Personal_Messages/Формат_DM_v1.md` - точный байтовый формат личных сообщений.
- `docs/Blockchain/README.md` - точка входа по форматам SHiNE-блокчейна.
- `docs/Solana_Architecture/README.md` - архитектура Solana-программ, PDA-счетов, DAO и движения средств.
- `docs/Инициализация_Solana_регистрации/README.md` - деплой и первичная инициализация Solana-регистрации.
- `docs/Протоколы/SHINE_ARWEAVE_DERIVATION_V1.md` - derivation Arweave-кошелька из `client key`.
## Что нужно уточнить перед реализацией
@@ -0,0 +1,23 @@
# Экран проверки public Solana RPC в UI
- статус: `pending`
## Кратко
Добавлен отдельный экран разработчика для проверки публичных mainnet Solana RPC прямо из браузера.
Экран отправляет реальный JSON-RPC `getVersion` запрос к списку public endpoint-ов и показывает,
какие ноды реально подходят для browser use.
## Что проверять
- в `Настройки разработчика` появилась кнопка `Solana: проверить public RPC`;
- экран открывается без ошибок;
- по каждой mainnet ноде показывается итоговый статус;
- для доступных нод видно HTTP-статус, время ответа и версию `solana-core`;
- длинные URL и ошибки не распирают экран по ширине на телефоне.
## Ожидаемый результат
- можно визуально увидеть, какие public Solana RPC доступны именно из браузера;
- проверка работает без промежуточного сервера;
- экран остаётся mobile-first и не требует горизонтального скролла.
@@ -0,0 +1,20 @@
## Кратко
- Переделан финальный flow регистрации: сначала экран ожидания подтверждения Solana с прогрессом и повторными проверками, потом отдельный экран успешной регистрации с кнопкой входа в стандартный экран сохранения ключей.
## Что проверять
- После отправки регистрации открывается экран ожидания с текстом про подтверждение Solana и кликабельным `Tx ID`.
- Прогресс-бар сначала идёт быстрее, потом заметно замедляется.
- Первая проверка регистрации начинается примерно через 4 секунды, далее повторяется раз в 2 секунды.
- Пока подтверждения нет, снизу показываются понятные статусы ожидания.
- После подтверждения открывается экран «Поздравляем с регистрацией» с одной кнопкой `Войти в аккаунт`.
- Кнопка `Войти в аккаунт` открывает штатный экран сохранения ключей с тремя галочками.
- Стрелка назад в левом верхнем углу на обоих экранах возвращает в главное меню.
- После успешного сохранения ключей регистрационный черновик и адрес кошелька очищаются.
## Ожидаемый результат
- Пользователь не видит преждевременное поздравление до подтверждения регистрации в Solana.
- Финальный успех показывается только после появления подтверждения регистрации.
- Вход после регистрации идёт через тот же стандартный сценарий сохранения ключей, что и в обычном login-flow.
## Статус
- pending
+4 -4
View File
@@ -4,13 +4,13 @@
Точка входа:
- `Dev_Docs/Personal_Messages/Протокол_DM_v1.md` — логика протокола, роли API, серверное поведение, routing по `access_servers`
- `Dev_Docs/Personal_Messages/Формат_DM_v1.md` — точный бинарный формат контейнера `SHiNE_DM`
- `Dev_Docs/Personal_Messages/Технические_вставки_DM_v1.md` — формат специальных `<SHiNE:...>` вставок внутри plaintext DM после расшифровки
- `docs/Personal_Messages/Протокол_DM_v1.md` — логика протокола, роли API, серверное поведение, routing по `access_servers`
- `docs/Personal_Messages/Формат_DM_v1.md` — точный бинарный формат контейнера `SHiNE_DM`
- `docs/Personal_Messages/Технические_вставки_DM_v1.md` — формат специальных `<SHiNE:...>` вставок внутри plaintext DM после расшифровки
Исторический устаревший документ сохранён отдельно:
- `Dev_Docs/Personal_Messages/Спецификация_DM_v0.5_устаревшая.md`
- `docs/Personal_Messages/Спецификация_DM_v0.5_устаревшая.md`
Правило сопровождения:
@@ -20,15 +20,15 @@
Точный байтовый формат контейнера вынесен отдельно:
- `Dev_Docs/Personal_Messages/Формат_DM_v1.md`
- `docs/Personal_Messages/Формат_DM_v1.md`
Формат клиентских технических вставок внутри plaintext вынесен отдельно:
- `Dev_Docs/Personal_Messages/Технические_вставки_DM_v1.md`
- `docs/Personal_Messages/Технические_вставки_DM_v1.md`
Устаревшая предыдущая версия сохранена отдельно:
- `Dev_Docs/Personal_Messages/Спецификация_DM_v0.5_устаревшая.md`
- `docs/Personal_Messages/Спецификация_DM_v0.5_устаревшая.md`
## 1. Основная модель
@@ -61,7 +61,7 @@
Подробности стандартного преобразования:
- `Dev_Docs/Протоколы/Преобразование_ED25519_в_X25519.md`
- `docs/Протоколы/Преобразование_ED25519_в_X25519.md`
### 2.2. Правило шифрования копий
@@ -6,7 +6,7 @@
Aктуальная целевая спецификация:
- `Dev_Docs/Personal_Messages/Протокол_DM_v1.md`
- `docs/Personal_Messages/Протокол_DM_v1.md`
Что в этом документе считать устаревшим:
@@ -35,7 +35,7 @@ Aктуальная целевая спецификация:
Черновик будущих вложений вынесен отдельно:
- `Dev_Docs/Personal_Messages/Черновик_будущих_DM_вложений.md`
- `docs/Personal_Messages/Черновик_будущих_DM_вложений.md`
## Общая схема
+1 -1
View File
@@ -12,7 +12,7 @@
Логика протокола, API и поведение сервера описаны отдельно:
- `Dev_Docs/Personal_Messages/Протокол_DM_v1.md`
- `docs/Personal_Messages/Протокол_DM_v1.md`
## 1. Общие правила
+1 -1
View File
@@ -2,7 +2,7 @@
Документ описывает целевой формат пользовательской PDA-записи `user_pda` для Solana-программы `shine_users`.
Это не формат основного блокчейна SHiNE и не документация по `AddBlock`. Основной блокчейн SHiNE описан отдельно в `Dev_Docs/Blockchain/`.
Это не формат основного блокчейна SHiNE и не документация по `AddBlock`. Основной блокчейн SHiNE описан отдельно в `docs/Blockchain/`.
Статус документа: итоговый согласованный формат, к которому приведены `create_user_pda`, `update_user_pda` и тестовый сериализатор Solana-модуля.
+2 -2
View File
@@ -8,7 +8,7 @@
Связанные документы:
- `Dev_Docs/Инициализация_Solana_регистрации/README.md` — single source of truth по деплою и первичной инициализации регистрации пользователей.
- `docs/Инициализация_Solana_регистрации/README.md` — single source of truth по деплою и первичной инициализации регистрации пользователей.
- `shine-solana/shine/doc/formats/shine-user-pda-format-v.1.0.md` — точный формат `user_pda` для `shine_users`.
- `shine-solana/shine/doc/FUNDS_FLOW.md` — короткая справка по денежным потокам внутри Solana-модуля.
@@ -55,7 +55,7 @@ DAO в текущем виде не является отдельной Anchor-
1. `shine-solana/shine/Anchor.toml`
2. `declare_id!` в `programs/*/src/lib.rs`
3. `programs/common/src/deploy_config.rs`
4. UI/серверные константы, перечисленные в `Dev_Docs/Инициализация_Solana_регистрации/README.md`
4. UI/серверные константы, перечисленные в `docs/Инициализация_Solana_регистрации/README.md`
## Ключи и authority
-97
View File
@@ -1,97 +0,0 @@
# ИТХ — ежедневное закрытие блокчейна SHiNE (кратко, для людей)
Статус: дизайн согласован, код не написан.
Подробная спецификация для реализации: [Спецификация_ИТХ_v1.md](./Спецификация_ИТХ_v1.md).
## Что это
Раз в сутки один из серверов SHiNE («дежурный») делает **закрытие дня**:
1. Строит меркл-дерево по вершинам всех пользовательских цепочек —
его корень называется **ИТХ** (итоговый хэш дня).
2. Собирает **бандл** — все новые блоки, появившиеся с прошлого закрытия,
с хэшами и подписями пользователей.
3. Заливает всё это одним файлом (**чекпоинт**) в Arweave.
Каждый чекпоинт ссылается на предыдущий — получается цепочка чекпоинтов.
4. Пишет в свой Solana PDA: «крайний чекпоинт — вот такой хэш, лежит по такому Arweave-адресу».
5. Публикует пост о закрытии в своём канале закрытий прямо в Сиянии.
Остальные серверы проверяют чекпоинт, и если всё сходится:
- обновляют слот в **своём** PDA на этот же чекпоинт («согласен»);
- ставят **лайк** на пост дежурного в канале.
Несогласие = просто ничего не делать (слот остаётся на прошлом чекпоинте).
## Зачем
- **Неизменяемость истории.** Прошлые дни зафиксированы в Arweave и Solana —
их нельзя переписать даже сговором всех живых серверов.
- **Независимая проверяемость.** Любой желающий может скачать цепочку чекпоинтов
из Arweave и проверить всю историю, не доверяя ни одному живому серверу.
- **Резерв последней надежды.** Даже если все серверы погибнут,
история до последнего чекпоинта не потеряна.
В повседневной работе серверы синхронизируются друг с другом напрямую —
Arweave почти не читается, это гарантия, а не рабочая лошадка.
## Кто дежурит
График ведёт DAO в специальном PDA: упорядоченный список логинов серверов
и дата начала эпохи. Дежурный дня = `dayIndex % количество_серверов`.
Обновлять список может только DAO.
## Расписание суток (UTC)
| Время | Что происходит |
| --- | --- |
| День D, 00:00–24:00 | Обычная работа, блоки копятся. |
| D+1, 00:0006:00 | Дежурный закрывает день D: Arweave → PDA → пост в канал. |
| D+1, до 24:00 | Остальные проверяют и подтверждают (PDA + лайк). |
Если дежурный не закрыл (или закрыл неверно) — день просто пропускается:
никто ничего не подтверждает, а следующий дежурный включает в свой бандл
блоки за все пропущенные дни и ссылается на последний признанный чекпоинт.
## Как понять, какой чекпоинт «настоящий»
**Актуальный чекпоинт = последний, на который указывают слоты
больше половины серверов из графика.** Кворума on-chain нет —
каждый сервер публикует только своё мнение, а наблюдатель считает голоса сам.
Сервер, который систематически ошибается, DAO просто исключает из списка.
Правило работает относительно списка: по умолчанию считают по DAO-списку,
но любой наблюдатель вправе считать голоса по **собственному списку доверенных
серверов**. Коротко: запись — по общему списку, чтение — по любому.
## Форки и параллельные группы
- Пользователь раздвоил свою цепочку до чекпоинта — дежурный возьмёт самую
длинную ветку, проигравшая отбросится; пользователь не наказывается.
- Ветка против уже зачекпоинченного — побеждает чекпоинт, ветка отбрасывается.
- Умудрился дописать противоречащие ветки в архивы **двух разных групп закрытий**
цепочка замораживается навсегда (два неизменных архива слить нельзя),
дальше пользователь начинает новую цепочку `login-002`.
- Любая другая группа серверов может вести свою параллельную цепочку чекпоинтов
над теми же данными — это разрешено by design: подделать блоки они не могут,
а независимых «нотариусов» у истории становится больше.
## Восстановление сервера с нуля
1. Прочитать DAO PDA → список серверов графика.
2. Прочитать их PDA-слоты → последний признанный чекпоинт (>половины голосов).
3. Скачать из Arweave цепочку чекпоинтов от генезиса и раскатать бандлы.
4. «Хвост» после последнего чекпоинта добрать живой синхронизацией с серверами.
## Кто платит
Каждый сервер платит сам: за свои Arweave-загрузки в свои дежурные дни
и за свои Solana-транзакции.
## Связанные документы
- [Спецификация_ИТХ_v1.md](./Спецификация_ИТХ_v1.md) — точный формат и алгоритмы (для реализации).
- `docs/Blockchain/` — формат блоков и цепочек SHiNE.
- `docs/Blockchain/sync-between-servers.md` — живая межсерверная синхронизация.
- `docs/Solana_Architecture/README.md` — устройство Solana-части.
- `shine-solana/shine/doc/formats/shine-user-pda-format-v.1.0.md` — формат `user_pda`.
@@ -1,398 +0,0 @@
# Спецификация ИТХ v1 — ежедневное закрытие блокчейна SHiNE
Статус: дизайн согласован пользователем, реализации в коде нет.
Краткое описание для людей: [README.md](./README.md).
Документ рассчитан на то, чтобы по нему можно было написать код без дополнительных
уточнений. Все байтовые порядки — big-endian (BE), все хэши — SHA-256 (32 байта),
все подписи — Ed25519, все времена — UTC.
---
## 1. Термины
| Термин | Значение |
| --- | --- |
| **День D** | Календарные сутки UTC `[D 00:00:00; D+1 00:00:00)`. |
| **dayIndex** | Порядковый номер суток от эпохи графика: `dayIndex = (началоСуток_D epochStart) / 86400_000`. У `epochStart` dayIndex = 0. |
| **Дежурный** | Сервер, закрывающий день D: `rota.servers[dayIndex mod rota.servers.length]`. |
| **Чекпоинт** | Артефакт закрытия дня: контейнер (заголовок + state-список + бандл), одним файлом в Arweave. |
| **ИТХ** | Итоговый хэш дня = `stateRoot` — меркл-корень по вершинам всех цепочек. |
| **Бандл** | Все блоки, не покрытые предыдущим признанным чекпоинтом. |
| **checkpointHash** | SHA-256 канонического preimage заголовка (раздел 5.3). Идентификатор чекпоинта. |
| **Признанный чекпоинт** | Последний чекпоинт, на который указывают PDA-слоты больше половины серверов графика. |
## 2. График дежурств (rota PDA, DAO)
Отдельный PDA, создаётся и обновляется **только DAO authority**
(точная реализация — в Solana-модуле `shine-solana/shine/`, отдельным решением;
предлагаемые seeds: `["checkpoint_rota"]`).
Логическое содержимое:
```
RotaV1
- version: u8 = 1
- epoch_start_utc_day: u32 // число суток от unix-эпохи (00:00 UTC)
- servers_count: u8
- servers: [ login_len: u8, login: UTF-8 ] * servers_count // упорядоченный список
```
Правила:
- Дежурный дня D определяется по состоянию rota PDA на момент `00:00 UTC дня D+1`
(момент начала окна закрытия). Изменение списка не пересчитывает прошлые дни.
- Вход в график — **по доверию**, не за деньги: DAO ведёт список как временный
уровень, пока не сформировано сообщество сияющих; целевое состояние —
приём/исключение серверов через голосование сияющих (см. TODO).
- При изменении состава серверов будущее распределение сдвигается автоматически —
это допустимо, календарь «дата → сервер» не ведётся.
## 3. Расписание суток (UTC)
| Окно | Действие |
| --- | --- |
| `D+1 00:00 06:00` | Дежурный строит чекпоинт дня D, заливает в Arweave, обновляет свой PDA-слот (`role=closed`), публикует пост в канале закрытий. |
| `D+1 06:00 24:00` | Остальные серверы проверяют; при успехе — свой PDA-слот (`role=agreed`) + лайк на пост. Проверять можно и раньше 06:00, как только чекпоинт появился. |
- Нет валидного чекпоинта дня D к началу следующего окна закрытия → день D **пропущен**:
никто не меняет свои слоты; дежурный дня D+1 включает блоки за все непокрытые дни
и ссылается `prev`-полями на последний признанный чекпоинт.
- Сервер, отстававший несколько дней, догоняет **последовательно**: проверяет цепочку
чекпоинтов по одному (каждый — относительно предыдущего) и в конце ставит свой слот
на последний проверенный. Опоздавшее согласие допустимо в любой момент.
## 4. Что попадает в бандл (правило среза)
- Срез = момент начала сборки чекпоинта дежурным (внутри окна `00:0006:00`).
- В бандл входят все блоки, известные дежурному на момент среза и **не покрытые
предыдущим признанным чекпоинтом**: для каждой цепочки `c` — блоки с номера
`prevState[c].lastNumber + 1` до локальной вершины; новые цепочки — целиком,
с первого блока (номер `0`).
- Блоки, созданные уже утром D+1 до среза, допустимо включать — это не ошибка.
- Полнота относительно других серверов **не требуется**: блоки, которых у дежурного
не было, войдут в следующий чекпоинт. Проверяющие не считают «у меня есть больше»
причиной несогласия.
- **Выбор ветки (fork-choice дежурного)**: если из вершины `prevState[c]` растут
несколько валидных веток (пользователь подписал противоречащие блоки), дежурный
берёт самую длинную валидную; при равной длине — ветку с меньшим `hash32` первого
расходящегося блока (байтовое сравнение). Ветки, не растущие из `prevState[c]`,
кандидатами не являются. Подробнее о форках — раздел 10.
## 5. Формат чекпоинта (один файл / одна Arweave-транзакция)
### 5.1. Контейнер
```
magic: 16 байт ASCII "SHiNE-CHECKPOINT" (без завершающего нуля)
headerLen: u32 BE — длина headerJson в байтах
headerJson: UTF-8 JSON (раздел 5.2)
bundle: записи бандла подряд (раздел 5.4), суммарной длиной до конца файла
```
Магик читаемый — открыв файл в hex-редакторе, видно, что это (по образцу
`"SQLite format 3\0"`). Версия формата в магик не входит: она в поле `v`
заголовка и в первой строке preimage (`SHiNE-CHECKPOINT-V1`).
Рекомендуемые Arweave-теги: `App-Name: SHiNE`, `Type: checkpoint`,
`Day-Index: <dayIndex>` (теги информационные, доверия к ним нет — истина в подписи).
### 5.2. headerJson
```json
{
"v": 1,
"dayIndex": 123,
"dayDate": "2026-07-12",
"closedBy": "serverlogin",
"closedAtMs": 1789200000000,
"serverPubKeyB58": "…",
"prevCheckpointHashHex": "…64 hex… | null",
"prevArweaveTxId": "… | null",
"stateRootHex": "…64 hex…",
"stateCount": 3500,
"bundleRootHex": "…64 hex…",
"bundleCount": 4200,
"state": [
{ "name": "alice-001", "lastNumber": 17, "lastHashHex": "…64 hex…" }
],
"signatureB64": "…"
}
```
- `state` отсортирован по `name` **байтово по UTF-8, по возрастанию**, без дублей.
- `dayDate` — дата дня D (для читаемости; проверяется соответствие `dayIndex`).
- Для генезис-чекпоинта `prevCheckpointHashHex` и `prevArweaveTxId` равны `null`,
`prevState` считается пустым, бандл содержит всю историю.
- Все hex — в нижнем регистре.
### 5.3. Канонический preimage, checkpointHash и подпись
Preimage — UTF-8 строка из 9 строк, разделитель `\n` (без завершающего `\n`;
`null`-значения кодируются как `-`):
```
SHiNE-CHECKPOINT-V1
<dayIndex>
<dayDate>
<closedBy>
<prevCheckpointHashHex | ->
<prevArweaveTxId | ->
<stateRootHex>
<bundleRootHex>
<closedAtMs>
```
- `checkpointHash = SHA-256(preimage)`.
- `signatureB64 = Ed25519(preimage)` актуальным клиентским ключом дежурного сервера
(ключ из его `user_pda`, действующий на момент закрытия; `serverPubKeyB58` его дублирует).
- `checkpointHash` не зависит от форматирования JSON — только от полей preimage.
### 5.4. Запись бандла
Записи идут подряд, отсортированы по `(name байтово, blockNumber)` по возрастанию:
```
nameLen: u16 BE
name: UTF-8 (blockchainName)
blockNumber: u64 BE
blockLen: u32 BE
blockBytes: полный блок как в хранилище: preimage + sigMarker + signature64
```
`bundleCount` в заголовке = число записей; парсер обязан проверить, что записи
заканчиваются ровно на конце файла.
### 5.5. Меркл-правила (общие для state и бандла)
- Лист: `SHA-256(0x00 || данные листа)`.
- Узел: `SHA-256(0x01 || left32 || right32)`.
- При нечётном числе узлов на уровне последний **поднимается без изменения**.
- Пустое дерево → 32 нулевых байта.
Данные листа:
- **state** (порядок листьев = порядок `state`):
`nameBytes || 0x00 || lastNumber u64 BE || lastHash32`.
Корень = `stateRoot` = **ИТХ**.
- **бандл** (порядок листьев = порядок записей): полные байты записи из 5.4
(включая nameLen/name/blockNumber/blockLen). Корень = `bundleRoot`.
## 6. Слот чекпоинта в PDA сервера
Новый типизированный блок в `user_pda` сервера (рядом с `ServerProfileBlock (30)`;
финальный номер `block_type` и правки `shine-user-pda-format-v.1.0.md`
при реализации Solana-части, отдельным решением). Предложение:
```
CheckpointStateBlock (block_type = 60, block_version = 0)
- day_index: u32
- checkpoint_hash: [32]
- arweave_tx_len: u8, arweave_tx: ASCII
- closed_by_len: u8, closed_by: UTF-8
- my_role: u8 // 1 = closed (я закрывал), 2 = agreed (я согласен)
- updated_at_ms: u64
```
Правила:
- Слот **один**, перезаписывается на месте; истории в PDA нет
(история — цепочка чекпоинтов в Arweave и посты в канале).
- Дежурный после заливки ставит свой чекпоинт с `my_role=1`.
- Проверивший и согласный сервер ставит тот же `(day_index, checkpoint_hash,
arweave_tx, closed_by)` с `my_role=2`.
- **Несогласие = не трогать слот.** Отдельной записи «против» нет.
Правило признания (для наблюдателей и восстановления):
> Актуальный чекпоинт = последний (максимальный `day_index`), на который указывают
> одинаковые `(day_index, checkpoint_hash)` в слотах **больше половины** серверов
> из rota PDA. Рекомендация клиентам: до достижения половины считать актуальным
> предыдущий признанный.
Правило признания — правило **чтения**, а не записи:
- дефолтное множество для подсчёта — список rota PDA;
- любой наблюдатель (клиент, сторонний сервер, восстановление) вправе применять то же
правило к **собственному списку доверенных серверов**; DAO-список — рекомендация
по умолчанию (конфиг сервера: `checkpoint.trust.logins`; пусто = rota PDA);
- серверы графика при закрытии и проверке обязаны использовать общий DAO-список,
иначе ротация теряет общую точку отсчёта. Коротко: **запись — по общему списку,
чтение — по любому**.
Параллельные группы закрытий допустимы by design: любая другая группа серверов может
вести собственную цепочку чекпоинтов над теми же данными (свой генезис, свои PDA-слоты,
свои Arweave-транзакции). Группы механически не пересекаются (разные `prev`-цепочки)
и не могут подделать содержимое блоков — только заверять историю или не включать её
часть. Запрещать это кодом сознательно не планируется; «официальность» DAO-группы
держится только на том, что её список — дефолт в клиентах.
## 7. Канал закрытий в Сиянии
- У каждого сервера графика — свой публичный канал `type=1`, slug `checkpoints`
(создаётся обычным `TECH_CREATE_CHANNEL`).
- При закрытии дежурный пишет в свой канал `TEXT_POST`:
```
/.checkpoint <dayIndex> <checkpointHashHex> <arweaveTxId>
```
- Согласие другого сервера — `REACTION_LIKE`, таргетирующий блок этого поста.
Отзыв согласия — `REACTION_UNLIKE`. Комментарии не требуются.
- Смысл: человекочитаемое зеркало + криптографическая проверяемость через Arweave
(пост и лайки подписаны ключами серверов, зарегистрированными в Solana), с лагом в
один день (попадут в следующий бандл).
- Машинный якорь «здесь и сейчас» — PDA-слоты; канал вторичен.
- При реализации обязательно дописать команду `/.checkpoint` в
`docs/Blockchain/02_Channel_Commands.md`.
## 8. Алгоритм дежурного (закрытие дня D)
1. В окне `D+1 00:0006:00` убедиться по rota PDA, что дежурный — я.
2. Определить предыдущий признанный чекпоинт (свой слот + слоты остальных, раздел 6);
загрузить его `state` (локальный кэш или Arweave). Для генезиса — пустой.
3. Зафиксировать срез: для каждой цепочки — вершина выбранной ветки
`(lastNumber, lastHash)` по правилу fork-choice из раздела 4.
4. Собрать бандл по разделу 4, отсортировать по 5.4, посчитать `bundleRoot`.
5. Сформировать `state` по срезу, отсортировать, посчитать `stateRoot` (ИТХ).
6. Собрать preimage, `checkpointHash`, подписать серверным ключом.
7. Залить контейнер одной Arweave-транзакцией (JWK сервера с хоста, вне git),
дождаться подтверждения, получить `arweaveTxId`.
8. Обновить свой PDA-слот: `(dayIndex, checkpointHash, arweaveTxId, closedBy=я, role=1)`.
9. Опубликовать пост `/.checkpoint …` в своём канале закрытий.
10. Любая ошибка на шагах 7–9 → повторять до конца окна; не успел — день пропущен,
ничего не публиковать частично (PDA-слот и пост ставить только после успешного Arweave).
## 9. Алгоритм проверяющего
Вход: обнаружен новый чекпоинт дня D от сервера S (по его PDA-слоту и/или посту).
1. **График**: `S == rota[dayIndex mod N]` по состоянию rota PDA на `00:00 D+1`;
`dayIndex` больше `day_index` последнего признанного локально чекпоинта.
2. Скачать Arweave-транзакцию из слота S; проверить `magic`, длины, парсинг.
3. Пересчитать preimage → `checkpointHash`; сверить со слотом S (и постом, если есть).
4. Проверить `signatureB64` ключом S (актуальный клиентский ключ из `user_pda` S;
допускается ключ, действовавший в день закрытия — см. TODO про ротацию).
5. `prevCheckpointHashHex`/`prevArweaveTxId` == локально признанный крайний чекпоинт.
Иначе — отказ.
6. Пересчитать `bundleRoot` по записям; сверить. Проверить сортировку и `bundleCount`.
7. **Мостик prev→state** (полнота бандла): для каждой цепочки из `state`
блоки бандла образуют непрерывную последовательность номеров и `prevHash`-ссылок
от `prevState[c]` (или от блока 0 для новой цепочки) ровно до `state[c]`.
Для каждого блока: `hash32 == SHA-256(preimage)`, структура валидна по правилам
`docs/Blockchain/01_Common_Block_Format.md`, подпись Ed25519 валидна ключом
владельца цепочки, действующим на день закрытия.
8. Пересчитать `stateRoot` по `state`; сверить (это и есть проверка ИТХ).
9. **Конфликты с локальными данными**: если локально для `(цепочка, номер)` есть блок
с другим `hash32` — побеждает версия чекпоинта: локальная ветка отбрасывается
(реорганизация), согласие НЕ отклоняется. Отказ по конфликту возможен только когда
чекпоинт противоречит уже признанному прошлому — это ловится шагом 7 (мостик не
соединится с `prevState`). «У меня есть блоки, которых нет в бандле» — тоже НЕ отказ.
10. Импортировать недостающие блоки бандла в локальное хранилище тем же валидационным
путём, что и межсерверная синхронизация (`sync-between-servers.md`).
11. Успех → обновить свой PDA-слот (`role=2`) и поставить лайк на пост S.
Отказ → не делать ничего on-chain; подробно залогировать причину
(рекомендуемый префикс лога: `CHECKPOINT_REJECT`).
Проверка детерминирована: два честных сервера с любым набором локальных блоков приходят
к одному вердикту. Проверяется валидность выбора дежурного и стыковка с прошлым
чекпоинтом, но не «длиннейшесть» выбранной ветки — её никто, кроме дежурного, проверить
не может, и условием согласия она не является.
## 10. Форки пользовательских цепочек
Форк = два блока одной цепочки с одинаковым `blockNumber`, разным `hash32` и валидными
подписями владельца. Случайно-честно такое не возникает: каждый блок подписывается
владельцем и ссылается на `prevHash`. Пара таких блоков — самодостаточная
криптографическая улика, проверяемая любым сервером локально, без арбитра и голосования.
Лесенка разруливания:
| Ситуация | Арбитр | Исход |
| --- | --- | --- |
| Ветки разошлись до чекпоинта | Дежурный (длиннейшая + tie-break, раздел 4) | Проигравшая ветка отбрасывается, цепочка живёт |
| Локальная ветка против зачекпоинченной в своей группе | Цепочка чекпоинтов | Локальная ветка отбрасывается (шаг 9.9) |
| Чекпоинт противоречит признанному прошлому | Проверяющие | Согласий нет, день пропущен |
| Противоречие между архивами двух групп закрытий | Никто — улика | Заморозка цепочки |
Заморозка применяется **только** в последнем случае — когда обе ветки уже зачекпоинчены
разными группами закрытий и неизменны в Arweave: слить два неизменных архива невозможно
в принципе.
- Улика: два конфликтующих блока, каждый из которых входит в валидный чекпоинт
своей группы.
- Сервер, проверивший улику, перестаёт принимать `AddBlock` в эту цепочку
(код отказа: `chain_frozen`); улика синхронизируется партнёрам как обычные данные,
каждый проверяет и замораживает независимо.
- Уже записанная история цепочки остаётся валидной и читаемой навсегда.
- Пользователь продолжает с новой цепочкой `<login>-<NNN+1>` (например, `login-002`);
`BlockchainRegistryBlock` в `user_pda` уже поддерживает несколько цепочек.
Старые связи/подписки указывают на корни старой цепочки — репутационная история
не переносится, это осознанная часть последствий.
Обязательное правило для клиентов (защищает честных пользователей от гонок):
повтор отправки блока после таймаута/ошибки — только **байт-в-байт** тем же блоком.
Байт-идентичный дубль — дедупликация, а не форк. Пересборка блока с новым `timestamp`
на тот же номер создаёт форк на ровном месте.
## 11. Восстановление сервера с нуля
1. Прочитать rota PDA → список серверов.
2. Прочитать их PDA-слоты → признанный чекпоинт (правило раздела 6).
3. От него по `prevArweaveTxId` спуститься до генезиса, затем раскатать бандлы
от генезиса вверх, валидируя каждый чекпоинт по разделу 9 (шаги 2–8).
4. Хвост после последнего чекпоинта добрать живой синхронизацией с серверами.
## 12. Стоимость и ключи
- Каждый сервер платит сам: Arweave — за свои дежурные дни, Solana — за свои слоты.
- Arweave JWK сервера хранится только на хосте (например,
`/home/player/SHiNE/secrets/…`), путь задаётся конфигом; в git не попадает —
по той же схеме, что `test.freeAvatar.walletJwkPath`.
## 13. Ориентир для реализации на сервере (не нормативно)
- `CheckpointScheduler` — таймер окон 00:00/06:00 UTC, определение роли по rota PDA.
- `CheckpointBuilder` — срез, бандл, меркл, preimage, подпись (разделы 4–5, 8).
- `ArweaveCheckpointUploader` — заливка контейнера, ожидание подтверждения.
- `CheckpointVerifier` — раздел 9; переиспользовать валидацию блоков из `AddBlock`/sync.
- `CheckpointSolanaSlot` — чтение/запись `CheckpointStateBlock`, чтение rota PDA.
- `CheckpointChannelPublisher` — пост `/.checkpoint` и лайки через обычный `AddBlock`.
- Конфиги (предложение): `checkpoint.enabled`, `checkpoint.arweave.jwkPath`,
`checkpoint.rota.pda` (или деривация по seeds), `checkpoint.closeWindowHours=6`.
- Локально хранить: `state` последнего признанного чекпоинта + его
`(dayIndex, hash, arweaveTxId)` — чтобы не ходить в Arweave на каждое закрытие.
## 14. TODO / сознательно отложено
- **История ключей при ротации**: если сервер/пользователь меняет ключ несколько раз
за день, для проверки старых подписей нужна история версий ключей. v1 живёт с
«ключ, актуальный на день закрытия»; при внедрении ротации — хранить версии.
- **Механизм новой цепочки `-002`**: пользовательский flow «продолжить с новой
цепочкой» после заморозки (регистрация в `BlockchainRegistryBlock`, UI) пока
не реализован — см. раздел 10.
- **Детерминированный выбор дежурного**:
`дежурный = SHA-256(prevCheckpointHash || dayIndex) mod N` по байтово отсортированным
логинам — убирает порядок списка как рычаг управления, DAO остаётся только
вход/выход участников.
- **Передача ведения графика сообществу**: вход в группу закрытий — по доверию,
не за деньги. Сейчас список ведёт DAO — это временный уровень, пока сообщество
сияющих не сформировано; в перспективе приём/исключение серверов — через
голосование сияющих. Депозитный/платный вход сознательно отвергнут.
- **Автопропуск штрафника** в ротации (2 подряд закрытия без единого согласия).
- **On-chain кворум** вместо правила чтения «>половины» — если понадобится.
- **Сжатие бандла** (gzip) и **чанкование** очень больших бандлов на несколько
Arweave-транзакций с манифестом. v1 — один несжатый файл.
- Экономия Solana-транзакций: считать согласия только по лайкам канала — возможно
после обкатки v1.
## 15. Связанные документы (обновлять при реализации)
- `docs/Blockchain/01_Common_Block_Format.md` — формат блока (используется в 5.4, 9.7).
- `docs/Blockchain/02_Channel_Commands.md` — дописать `/.checkpoint`.
- `docs/Blockchain/sync-between-servers.md` — путь импорта блоков (9.10).
- `shine-solana/shine/doc/formats/shine-user-pda-format-v.1.0.md` — дописать
`CheckpointStateBlock` и rota PDA при реализации Solana-части.
- `docs/API/04_Add_Block_to_Blockchain_API.md` — при реализации дописать код отказа
`chain_frozen` (раздел 10).
- `docs/Blockchain/CHANGELOG.md` — фиксировать изменения.
@@ -115,4 +115,4 @@ sha256$<hex( SHA-256("shine-pairing|" + lower(login.trim()) + "|" + password) )>
Для отдельного RPC-взаимодействия между браузерным wallet-расширением и ESP32 см. документ:
- [Формат_взаимодействия_внешнего_кошелька_и_ESP32.md](/home/ai/work/SHiNE/SHiNE-server-sha256/Dev_Docs/Протоколы/Формат_взаимодействия_внешнего_кошелька_и_ESP32.md)
- [Формат_взаимодействия_внешнего_кошелька_и_ESP32.md](/home/ai/work/SHiNE/SHiNE-server-sha256/docs/Протоколы/Формат_взаимодействия_внешнего_кошелька_и_ESP32.md)