feat: DSG Orchestrator: Trust Gate and Feed Transport (Phases 1–2a) + Federation Boundary Verification #2
No reviewers
Labels
No labels
Compat/Breaking
Kind/Bug
Kind/Documentation
Kind/Enhancement
Kind/Feature
Kind/Security
Kind/Testing
Priority
Critical
Priority
High
Priority
Low
Priority
Medium
Reviewed
Confirmed
Reviewed
Duplicate
Reviewed
Invalid
Reviewed
Won't Fix
Status
Abandoned
Status
Blocked
Status
Need More Info
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
WetGrape/GuildWine!2
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "kiper220/GuildWine:dsg-orchestrator"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Что в PR
Три независимых блока (3 атомарных коммита над baseline):
1. Верификация collision-guard (server)
P0-гард
ensure_remote_shadow_ids_freeуже стоял в коде. Добавлено:tests/social.rs);tests/federated_conversation_writes.rs);docs/11,docs/12).Production-логика не менялась (только rustfmt-реформат).
2. Federated call integration-тесты (server)
Two-home round-trip start/end звонка, idempotent duplicate, shadow-authority guard для calls-пути (
tests/calls.rs). Путь уже был реализован и врезан — теперь покрыт тестами.3. DSG-оркестратор: Фаза 1 + Фаза 2a (client-web)
Фаза 1 — Welcome trust-gate:
DsgProjectionStore+DsgAcceptedCommitVerifier+DsgWelcomeRosterPolicy(оба порта реализованы, exact-match, fail-closed). Врезан в production credential-policy: гард реальный, store пуст до Фазы 2 → join остаётся fail-closed (MLS_WELCOME_ROSTER_MISMATCHвместоUNCONFIGURED).Фаза 2a — feed-transport: crash-safe DSG envelope feed (
device.sync.envelopes.pull/ack) — loop + page-parser + atomic IndexedDB opaque-inbox + realtime wake-веткаdevice_sync_events_available; врезан в репозиторий (durable-ингест opaque-конвертов).Статус / что НЕ в PR
DeviceSyncPayload-кодек/snapshot-reducer, наполнение projection-store и candidate-join — Фаза 2b/2c.DsgWelcomeBootstrap— это DSG application, нечитаемое non-member'ом). ДокументированныйDeviceAddOperationserver-transition flow (docs/09:325) разошёлся с реализацией и нуждается в уточнении перед 2c.Валидация
cargo test -p guildwine-server— social 7/7, federated_conversation_writes 5/5, calls 5/5;clippy -D warnings,fmt— чисто.build:types; apps/webtsc -b+ 14 тестов — зелёные.Ревью PR #2: DSG Orchestrator — Trust Gate + Feed Transport
Масштаб: 428 файлов, +131K строк. Основная масса — новый scaffolding монорепозитория (Rust server, client-web, Flutter, desktop-iced). Ключевой код — DSG Фаза 1+2a + тесты коллизий.
Безопасность — ✅ Solid
MLS_WELCOME_ROSTER_MISMATCHвместо обхода проверки. Нет лазейки через пустой callback.equalBytes) во всех сравнениях credential-байтов.WeakSet-маркировка — network JSON не может стать trust.Архитектура — ✅ Good
set_snapshot+get_for_update) — coherent reads внутри optimistic-transaction.close_after_responseдоставляет финальный ответ и блокирует reconnect.Замечания
dsg-feed.ts:197opaque_envelopeне валидирует exact keys, в отличие от других вариантовdsg-projection-store.ts:188equalBytesдублируется в 3 файлахevent_feed.rs:81Резюме
Качество кода очень высокое. Security-дизайн многоуровневый и fail-closed. Замечание #1 стоит поправить (консистентность валидации wire-формата), остальное — nits.
Вердикт: COMMENT — не блокирует. Исправление opaque_envelope валидации можно сделать в следующем PR.
✅ Good: Outbox byte budget (
max_buffered_bytes) + count limit (cap) + eviction policy (evict_oldest_ephemeralникогда не выкидывает reliable frames) — solid защита от memory exhaustion на медленных соединениях.close_after_responseдля self-revoke — аккуратный паттерн.✅ Good: DNS-free transport через
TcpStream::connect(SocketAddr)— адрес берётся только из конфига, не из запроса. Валидация endpoint (порт ≠ 0, не unspecified/multicast). TLS SNI используетFederationDomain, а не IP — WebPKI аутентификация сохраняется при IP-пиннинге.💡 Suggestion:
scheduled_throughс#[serde(default)]и нормализацией legacy-значений — хорошее backcompat-решение. Стоит добавить комментарий в migration docs, что старые БД до этого PR будут нормализовать0 → acknowledged_throughпри чтении.✅ Good:
set_snapshot(true)+snapshot.get_cfдля reads в сочетании сget_for_update_cf(true)для writes — даёт coherent view внутри транзакции. Комментарий точно описывает проблему: без snapshot, RocksDB может вернуть старое значение для первого ключа и новое для второго при конкурентном коммите.✅ Good: Welcome hash проверяется через
digestSha256(welcome)и сравнивается с декларированнымwelcome_hash. Сервер-side verifier (DsgAcceptedCommitVerifier) дополнительно проверяет evidence против projection store — two-layer защита от replay stale bootstrap material.🟡 Should fix: Кейс
opaque_envelopeне вызываетhasExactKeys, в отличие отbootstrap_requested(стр. 209) иdevice_revoked(стр. 220). Лишние поля в opaque-конверте будут молча проигнорированы вместо отклонения.Так как
request_idопционален, можно проверить оба варианта:Это поможет отловить протокольные изменения на ранней стадии.
✅ Good: Fail-closed дизайн безупречный — пустой store →
getRosterвозвращаетundefined→sameCredentialSetне вызывается →authorizeExactRosterвозвращаетfalse→ Welcome join падает сMLS_WELCOME_ROSTER_MISMATCH. Нет лазейки через пустой callback.💡 Suggestion:
equalBytesдублируется вdsg-projection-store.ts,mls-credential-policy.tsиdsg-bootstrap.ts— три идентичные constant-time реализации. Можно вынести в общий utility-модуль (напримерcrypto-utils.ts), чтобы избежать расхождения при рефакторинге.Не блокирующее, но при росте кодовой базы три копии одного крипто-примитива — потенциальный источник ошибок.
✅ Good: Последовательная цепочка проверок Welcome provenance:
isAuthenticatedDsgWelcomeProvenance→ проверкаconversation/groupId/epoch/sponsor→candidateDeviceId === ownDeviceId→roster.authorizeExactRoster. Каждое звено fail-closed. ИспользованиеWeakSetдля non-forgeable provenance — элегантное runtime-ограничение.Ревью PR #2 — детальные замечания
(Inline-комментарии не прикрепились через API — публикую здесь)
🟡 Should fix
1.
packages/client-web/src/dsg-feed.ts:197— кейсopaque_envelopeне вызываетhasExactKeysВ отличие от
bootstrap_requested(стр. 209) иdevice_revoked(стр. 220), которые валидируют точный набор ключей,opaque_envelopeмолча игнорирует лишние поля. Это нарушает консистентность wire-валидации и может пропустить протокольные изменения:💡 Suggestions
2.
packages/client-web/src/dsg-projection-store.ts:188—equalBytesдублируетсяConstant-time
equalBytesидентично реализован в трёх файлах:dsg-projection-store.ts,mls-credential-policy.ts,dsg-bootstrap.ts. Стоит вынести в общийcrypto-utils.ts— три копии крипто-примитива = риск расхождения при рефакторинге.3.
crates/server/src/infrastructure/persistence/event_feed.rs:81—scheduled_throughmigration note#[serde(default)]с нормализацией0 → acknowledged_through— хорошее backcompat-решение. Стоит задокументировать в migration docs, что старые БД до этого PR будут нормализовать поле при первом чтении.✅ Что хорошо
dsg-projection-store.ts:165): пустой store →MLS_WELCOME_ROSTER_MISMATCHвместо bypass. Нет лазейки через пустой callback.mls-credential-policy.ts:150-210):WeakSet-маркировка → каждый уровень проверяется → все fail-closed.dsg-bootstrap.ts:115): SHA-256 проверяется дважды — при парсинге и через server-side verifier.txn.rs:53):set_snapshot(true)+get_for_updateдля coherent reads.transport.rs:60): TCP-адрес только из конфига, TLS SNI = domain, ALPN pinned.registry.rs:55): byte budget + eviction policy без потери reliable frames.registry.rs:revoke_device):close_after_responseдоставляет финальный ответ и блокирует reconnect.Резюме
Качество кода очень высокое. Security-дизайн многоуровневый и fail-closed. Единственное, что стоит поправить — консистентность
hasExactKeysв opaque_envelope. Остальное — nits на будущее.