**Доверие:** нулевое к любым `.md` документам в `Протокол/` (включая `AUDIT.md`, `VERSION.md`, `ROADMAP.md`, `SPEC_DEVIATIONS.md`); полное к исходному коду в `Протокол/Code/montana-node` и его рабочей области, плюс к выводу собственных сборок и тестов.
Аудит подтверждает: реализация M1…M5 готова к внешнему security-аудиту специализированной фирмой по консенсусным протоколам с post-quantum криптографией. Открытых блокеров кода — нет. Открытые задачи относятся к (а) deployment-ceremony перед mainnet (Genesis bootstrap pubkeys), (б) интеграционному слою montana-node при переходе из singleton в multi-node (часть полей `ProposalHeader` сейчас placeholder), (в) расхождениям между AUDIT.md и реальным состоянием кода (документация AUDIT.md устарела).
---
## 2. Метод и принцип нулевого доверия
Каждое утверждение ниже подкреплено либо ссылкой на конкретный файл и строку из исходного кода, либо логом запущенной мной команды. Я НЕ принимал на веру:
- содержимое `AUDIT.md` (использовал как сырой target scope);
- статусы в `SPEC_DEVIATIONS.md` (проверял реальное состояние кода);
- собственную память о протоколе из предыдущих сессий;
- утверждения в комментариях кода о соответствии спеке (сверял с другими местами в коде, c byte-exact testами, с RFC и FIPS векторами).
**Тройная сверка:**
- *спека ↔ код* — численные константы, размеры структур, имена доменных разделителей, формулы;
- *код ↔ исполнение* — прогон полного набора тестов в один поток (`.cargo/config.toml [build] jobs = 1` + `RUST_TEST_THREADS = 1`);
Из соображений охраны машины автора (см. `feedback_single_core_tests.md`) все сборки и тесты выполнялись в одном процессе и одном потоке — это требование зашито в `[.cargo/config.toml](Code/.cargo/config.toml)`.
Динамическую инспекцию production deployment на серверах Frankfurt/Moscow я **не** выполнял: согласно `MEMORY.md → SeaFare Montana`, оба сервера обслуживают SeaFare и Cascade-VPN, не реализацию TimeChain. Production deployment montana-node ещё не существует.
---
## 3. Структура проверенной кодовой базы
15 крейтов, единая рабочая область Cargo. Ключевые зависимости пинованы точно через `[workspace.dependencies]` и `=X.Y.Z`:
- 4 блока обращений к FFI: контракт размеров явно описан, OpenSSL EVP convention `(void*)seed/sk/pk` — backwards compat cast без mutation, описано в [csrc/mt_crypto.c:61-67](Code/crates/mt-crypto-native/csrc/mt_crypto.c);
-`from_array(mut bytes)` принимает массив by-value, копирует в heap через `alloc_locked_secret_box`, **зануляет** stack-копию через `bytes.zeroize()` ([line 139](Code/crates/mt-crypto/src/lib.rs));
**13/13 security invariants Pass 17** ([crates/mt-crypto/tests/security_invariants.rs](Code/crates/mt-crypto/tests/security_invariants.rs)): trait-based проверки `!Clone`, `!PartialEq`, heap-allocation, наличие Drop, отсутствие `println!`/`log::*` на secret bytes — выполняются в CI как regression-detection.
**Привязка hash-композиции к domain-разделителям** ([mt-crypto/src/lib.rs:85-93](Code/crates/mt-crypto/src/lib.rs)): функция `hash(domain, parts)` всегда вставляет NUL-байт между доменом и payload — закрывает finding P1 (prefix-collision protection) предыдущего внешнего аудита.
-`WORDLIST_FINGERPRINT = SHA-256(WORDLIST_RAW)` зашит как константа;
-`init_wordlist()` ассертит совпадение, лексикографическую сортировку и ровно 2048 строк — не проходит инициализация при corruption встроенного wordlist;
-`pub fn word_index(word) -> Option<u16>` через `binary_search` (детерминирована).
| L-M1-1 | LOW | `mt-crypto-native::MLDSA65_SIGNATURE_SIZE = 3309` — дубликат `mt-crypto::SIGNATURE_SIZE`; обоснован для FFI boundary, но по строгому [C-1] SSOT может разъехаться при обновлении. | [crates/mt-crypto-native/src/lib.rs:7](Code/crates/mt-crypto-native/src/lib.rs) |
| L-M1-2 | LOW | `keypair()` тест-only функция использует `getrandom::getrandom().expect()` — `expect()` оправдан (OS CSPRNG fallback недоступности — environmental failure, не protocol invariant). Тем не менее, в `lib.rs` lib-кода это формально `.expect()`. Закрыт `cfg(any(test, feature = "testing"))`. | [crates/mt-crypto/src/lib.rs:270](Code/crates/mt-crypto/src/lib.rs) |
| INFO-M1-3 | INFO | TODO для `mt-telemetry` интеграции (F-5 closure pending) — runtime warning при `mlock` failure. Текущая реализация silently fallback-ит на non-locked Box. Не security gap (encrypted swap покрывает) — но видимость для оператора отложена. | [crates/mt-crypto/src/lib.rs:183-189](Code/crates/mt-crypto/src/lib.rs) |
| INFO-M1-4 | INFO | M1-крейт `keypair_from_seed_mlkem` не покрыт собственными NIST KAT для Encapsulate/Decapsulate (только KeyGen). Fixtures для encapDecap есть в репо, но тест не интегрирован. Не блокер: ML-KEM Encapsulate/Decapsulate понадобится в M6+ application layer. | (отсутствие теста) |
### 4.4 Рекомендации по M1
1.**(LOW)** перевести `mt-crypto-native::MLDSA65_SIGNATURE_SIZE` на `pub use mt_crypto::SIGNATURE_SIZE` либо явно задокументировать что это reverse-dependency (мне видится первое предпочтительнее);
2.**(INFO)** добавить ML-KEM-768 Encapsulate/Decapsulate KAT при разработке M6+ messaging слоя;
3.**(INFO)** ускорить `mt-telemetry` интеграцию для observability `mlock` failure.
### 4.5 Оценка M1: **9.0 / 10**
Это исключительно сильный криптографический слой. Hybrid Rust+C через own thin FFI wrapper — правильный архитектурный выбор по [C-6], учитывая отсутствие production-grade pure-Rust имплементаций ML-DSA / ML-KEM в 2026-Q2. Production audit firms (NCC Group, Trail of Bits, Cure53) могут принимать этот код в работу немедленно.
---
## 5. Фаза M2 — основа состояния (state foundation)
**Состояние (`mt-state`)**: три таблицы (AccountTable, NodeTable, CandidatePool) с одинаковым pattern: `BTreeMap` + `SparseMerkleTree`. `compute_state_root` через SHA-256 с domain separator `mt-state-root` ([crates/mt-state/src/lib.rs:272-281](Code/crates/mt-state/src/lib.rs)). `is_active(node, W, τ₂)` через `saturating_sub` ([line 295](Code/crates/mt-state/src/lib.rs)) — защита от underflow в genesis окне.
-`checked_mul` overflow-detection через panic с descriptive message ([lines 49-58, 62-69](Code/crates/mt-timechain/src/lib.rs)) — корректный engineering halt при non-attacker triggered overflow (median_ratio derived канонически из cemented set, ≈ 1.5M лет до достижения горизонта);
- **`cemented_bundle_aggregate`** ([lines 90-111](Code/crates/mt-timechain/src/lib.rs)): три ветви (W <2→0×32genesis,|cemented|=0→`mt-bc-aggregate-empty`domain,иначе→`mt-bc-aggregate`сcanonicalsortednode_ids+window).**Signature и op_hashes ИСКЛЮЧЕНЫ из input**—закрытиеgrindingsurfaceчерезσконструктивно(нетвtypesignatureфункции).
| INFO-M2-3 | INFO | `compute_subtree_root::Combine` использует `expect()` (lines 122-126 в `mt-merkle`) — но это invariant breach в вычислительной структуре, не reachable от внешнего входа. Acceptable. | [crates/mt-merkle/src/lib.rs:122-126](Code/crates/mt-merkle/src/lib.rs) |
5 вызовов `panic!` ([lines 360, 378, 386, 392, 428, 438, 442, 475, 479, 496, 500, 580, 599, 632](Code/crates/mt-account/src/lib.rs)) обёрнуты `checked_sub/add().unwrap_or_else(|| panic!("protocol invariant breach: ..."))`с описательными сообщениями. Они НЕ reachable от подписанного external input если caller вызывает `validate_*` first (заявлено в комментарии lines 346-348). Это appropriate engineering halt при implementation bug — `expect()` стиль, но с лучшими error-сообщениями. Соответствует политике `Code/CLAUDE.md → Code style → No unwrap/expect в lib коде, кроме случаев protocol violation с явным комментарием почему invariant не может быть нарушен`.
| L-M3-1 | LOW | `op_height: u32` overflow horizon — формально 4.29 млрд операций одного аккаунта. На 1B пользователей это 4 op/user в среднем; при горячих аккаунтах (узлы-операторы, exchange-аккаунты) horizon может быть достижим в multi-decade scale. Текущий panic корректен (encoded arithmetic horizon), но может потребовать `u64` миграцию ранее заявленного «8000 лет» горизонта. | [crates/mt-account/src/lib.rs:386,392,475,479,496,500](Code/crates/mt-account/src/lib.rs) |
| INFO-M3-2 | INFO | Чистое разделение `settle_window` отдельно от `apply_proposal` ([line 543](Code/crates/mt-account/src/lib.rs)) хорошо документировано (orchestration ordering invariant виден caller-у), но требует дисциплины caller-а — нет typestate enforcement. Documented в module-level comment + `Code/CLAUDE.md → [C-7]`. | [crates/mt-account/src/lib.rs:711-720](Code/crates/mt-account/src/lib.rs) |
1.**(INFO)** добавить «Sensitivity at high transaction rate» расчёт `op_height` overflow horizon в Storage Card по [I-14] методологии. Если horizon достигается раньше ~50 лет на горячем аккаунте — рассмотреть upgrade path в M6+;
2.**(INFO)** добавить debug_assert либо комментарий `// PRECONDITION: validate_* called` в начале каждого `apply_*` для большей очевидности validate→apply ordering для будущих контрибьюторов.
### 6.5 Оценка M3: **9.0 / 10**
Excellent apply-layer. Полное покрытие validate→apply, корректные checked-arithmetic защиты, settle_window отделён от apply_proposal с явным orchestration contract.
**`determine_winner`** ([line 434-448](Code/crates/mt-lottery/src/lib.rs)): canonical tie-break `(ticket asc, class asc, id lex asc)` — устраняет недетерминизм при tie вероятностью 2⁻¹²⁸.
**`canonical_proposer`** ([crates/mt-consensus/src/lib.rs:185-203](Code/crates/mt-consensus/src/lib.rs)) — Lookback Leadership с явным **degraded-mode failsafe** документированным в комментарии M4-INFO-10: при empty W-2 cemented set → bootstrap_node_id (defense-in-depth liveness, не steady-state design).
**`apply_noderegistrations_batch`** ([crates/mt-entry/src/lib.rs:376-417](Code/crates/mt-entry/src/lib.rs)): incremental sort by `nr_sort_key` + canonical apply порядок; pending_count инкрементится после каждого insert для корректности `required_vdf_length` adaptive formula.
| M-M4-1 | MEDIUM | `BundleError::TooManyOps`/`TooManyReveals` cap = 65 535 (u16). На 1B пользователей при ~1000 узлах одно окно может содержать > 100K cemented op_hashes per node bundle. **Эскалация требует spec-patch на u16→u32 length prefix**. Документировано в комментарии line 122-124 как «SCALE NOTE (M6+ scaling concern)». **Это известный scale-limit, не сейчас-актуальный block.** | [crates/mt-lottery/src/lib.rs:122-129](Code/crates/mt-lottery/src/lib.rs) |
| L-M4-2 | LOW | `validate_winner` ([crates/mt-consensus/src/lib.rs:374-395](Code/crates/mt-consensus/src/lib.rs)) строго отвергает любой winner_id если cemented set W-1 пуст. Caller responsibility skip для genesis okon (M4-MED-2 documented). Это **не баг**, но усложняет caller контракт — нет typestate enforcement. | [crates/mt-consensus/src/lib.rs:374-395](Code/crates/mt-consensus/src/lib.rs) |
| L-M4-3 | LOW | `mt-entry::apply_selection_event` ([line 260-303](Code/crates/mt-entry/src/lib.rs)) — устанавливает `chain_length = 1` для нового узла (per spec invariant DS-2). Если caller вызывает функцию повторно для одного и того же селекшна (например через replay), `node_table.insert(node_record)` перезапишет существующий с chain_length, который мог быть выше. Защита: `pool.remove(&cand.node_id)` гарантирует что при втором вызове `selected` = empty (нет в Candidate Pool). Семантически корректно. | [crates/mt-entry/src/lib.rs:269-302](Code/crates/mt-entry/src/lib.rs) |
| INFO-M4-4 | INFO | `compute_control_set` (mt-consensus) включает в filter `c.cemented_window > previous_proposal_window AND ≤ current_window` — hard inclusive границы. Корректно для steady-state, но при reorg либо resync может потребовать дополнительных проверок (M6+ network-layer concern). | [crates/mt-consensus/src/lib.rs:248-267](Code/crates/mt-consensus/src/lib.rs) |
| INFO-M4-5 | INFO | `lottery_weight = chain_length_snapshot + seniority_bonus` ([crates/mt-lottery/src/lib.rs:257-259](Code/crates/mt-lottery/src/lib.rs)). `seniority_bonus = chain_length / 69`. 69 — magic number; обоснование в спецификации не прочитано (нулевое доверие к доке). Тестами зафиксировано — детерминированно. | [crates/mt-lottery/src/lib.rs:249-251](Code/crates/mt-lottery/src/lib.rs) |
1.**(MEDIUM)** запланировать M6+ spec-patch на u16 → u32 length prefix для bundle op_hashes/reveal_hashes; для текущего scope — добавить telemetry alert при `op_hashes.len() > 32 768` (early-warning перед достижением u16 cap);
2.**(LOW)** рассмотреть введение typestate wrapper `ValidatedHeader<H, W>` для force-обеспечения validate→apply ordering на уровне типов вместо документации;
3.**(INFO)** проверить документацию обоснования magic number 69 в seniority_bonus при будущей spec sweep.
### 7.5 Оценка M4: **8.5 / 10**
Очень сильный consensus слой. `log2_q64` integer log с Remez minimax — впечатляющая инженерия. M4-1 / M4-LOW-3 / M4-LOW-4 / M4-LOW-5 closures свидетельствуют что предыдущие audits были тщательны и closure качественное. Снижение на 0.5 за scale-concern u16 cap (известный, документированный, отложенный на M6+).
**Atomic write pattern** ([lines 95-114](Code/crates/mt-store/src/lib.rs)): `write_atomic` пишет в `<name>.tmp` затем `fs::rename(tmp, final)`. POSIX rename(2) atomic per single filesystem — observers видят либо old либо new content, никогда partial.
**Cleanup orphan tmp на open()** ([lines 49-71](Code/crates/mt-store/src/lib.rs)) — closure M5-LOW-8 finding. Защита от накопления tmp-файлов после crashed write_atomic (process killed между fs::write tmp и fs::rename).
**Strict CorruptedLength check ДО decode** ([lines 162-167, 201-206, 239-244, 364-370](Code/crates/mt-store/src/lib.rs)) — все decode_X функции отвергают неверный длиной payload до парсинга полей.
**Crash recovery через `verify_consistency`** ([lines 472-482](Code/crates/mt-store/src/lib.rs)): при reopen проверяет что proposal с window = meta.last_cemented существует в archive. Mismatch (crash между archive и meta write) → error, не silent skip.
**0 unsafe, 0 panic в production lib коде, 0 HashMap, 0 f32/f64.** SystemTime usage ([line 533](Code/crates/mt-store/src/lib.rs)) внутри `#[cfg(test)] mod tests` для `rand_suffix()` tempdir naming — НЕ в consensus path.
| L-M5-1 | LOW | `write_atomic` использует `fs::rename` для atomicity, но не вызывает `fsync`. Под power-loss POSIX rename atomic only after fsync **of the directory** (некоторые файловые системы могут потерять файл если rename committed но fsync не выполнен). Документировано в [line 92-93](Code/crates/mt-store/src/lib.rs) как «дополнительно использует fsync (в M6 operator layer); rename atomicity достаточна для filesystem-level consistency». **Это правильно для сейчас**, но при включении production оператор должен добавить fsync layer. | [crates/mt-store/src/lib.rs:87-114](Code/crates/mt-store/src/lib.rs) |
| INFO-M5-2 | INFO | `cleanup_orphan_tmp` — best-effort: при ошибке `read_dir` либо `fs::remove_file` просто skip ([lines 54-71](Code/crates/mt-store/src/lib.rs)). Tmp-накопление при много раз crashed open's возможно, но не security-critical. | [crates/mt-store/src/lib.rs:54-71](Code/crates/mt-store/src/lib.rs) |
1.**(LOW)** при переходе к production deployment добавить `fsync` для критичных commit-points (proposal archive + meta_last_cemented). Описано в комментарии line 92-94 как M6 operator layer задача — корректное deferral.
### 8.5 Оценка M5: **9.0 / 10**
Чистый minimal persistence layer. Atomic rename + cleanup tmp + crash recovery — правильный набор для filesystem-only хранения без heavyweight DB.
| DEV-003 | Лотерея отсутствует — winner = `state.nodes.iter().next()` | **закрыт**: формирование `VdfReveal` + `validate_reveal` + `BundledConfirmation` + `validate_bundle` + `weighted_ticket_node` через canonical API ([start.rs:232-286](Code/crates/montana-node/src/commands/start.rs)) |
| DEV-004 | BundledConfirmation никогда не формируется | **закрыт**: формируется и подписывается явно ([start.rs:256-268](Code/crates/montana-node/src/commands/start.rs)) |
| DEV-006 | state_root не cross-check между proposer и validator | **закрыт**: после `apply_proposal` recompute через `compute_state_root` и byte-exact compare; mismatch → panic ([start.rs:342-352](Code/crates/montana-node/src/commands/start.rs)) |
| DEV-007 | `next_d` не вызывается на τ₂ boundary | **закрыт**: вызывается в конце окна с τ₂ boundary check ([start.rs:393-406](Code/crates/montana-node/src/commands/start.rs)) — **с ограничением: см. ниже M-NODE-1** |
- **Production-grade naming** per [C-12]: `montana-node` crate, `org.montana.node` launchd label, `Montana/node/` path. Никаких маркеров `local/dev/test/temp/sim` в production identifiers.
| M-NODE-1 | MEDIUM | Hardcoded `median_permille = 1000u32` для `next_d` ([start.rs:394](Code/crates/montana-node/src/commands/start.rs)). Singleton всегда даёт 100% participation, поэтому D всегда увеличивается на +3% каждые τ₂ окна. Для multi-node M6+ потребуется реальная aggregation через cemented `BundledConfirmation` всех узлов. **Документировано как singleton-mode упрощение, не block.** | [crates/montana-node/src/commands/start.rs:393-406](Code/crates/montana-node/src/commands/start.rs) |
| L-NODE-1 | LOW | `IDENTITY_MAGIC = b"mt-local"` ([identity.rs:17](Code/crates/montana-node/src/identity.rs)) — file format magic для identity-файла. Имя `mt-local` создаёт naming clash с domain registry pattern `mt-*`. Это НЕ domain separator (используется только для file-format detection при load), но визуально может быть перепутан. | [crates/montana-node/src/identity.rs:17](Code/crates/montana-node/src/identity.rs) |
| L-NODE-2 | LOW | Double-sign pattern в start.rs: header подписывается на line 318-319 (с pre_state_root), затем replay-подписывается на line 339-340 (с post_state_root). Не security risk, но wasted ML-DSA-65 deterministic Sign call (≈ 5-50 ms на signature на commodity CPU). Можно оптимизировать — apply_proposal вычисляется ДО первой подписи, тогда header вооще ne нужно перевычислять. | [crates/montana-node/src/commands/start.rs:318-340](Code/crates/montana-node/src/commands/start.rs) |
| L-NODE-3 | LOW | `included_bundles_root = single_leaf_root(&bc_h)` где `single_leaf_root(leaf) = *leaf` ([line 515-517](Code/crates/montana-node/src/commands/start.rs)) — для одиночного включённого bundle возвращает hash без Merkle структуры. Семантически = root одного листа, но если spec требует full Merkle wrap (`leaf_hash(bc_h)`) — это может расходиться при interop. Singleton-mode workaround. | [crates/montana-node/src/commands/start.rs:288-289, 515-517](Code/crates/montana-node/src/commands/start.rs) |
| L-NODE-4 | LOW | `control_root = [0u8; 32]` ([line 290](Code/crates/montana-node/src/commands/start.rs)) — placeholder. В singleton нет control objects (нет других узлов регистрации); `[0; 32]` = empty Merkle tree marker. Корректно для пустого set, но проверить spec соответствие при появлении ControlObjects (M6+). | [crates/montana-node/src/commands/start.rs:290](Code/crates/montana-node/src/commands/start.rs) |
| INFO-NODE-5 | INFO | `target: u128::MAX` ([line 312](Code/crates/montana-node/src/commands/start.rs)) — placeholder. Singleton = 1 node = winner всегда побеждает (нет конкурентов). Real lottery semantics активируются при ≥ 2 узлах. | [crates/montana-node/src/commands/start.rs:312](Code/crates/montana-node/src/commands/start.rs) |
1.**(MEDIUM)** при переходе к multi-node M6+ заменить hardcoded `median_permille = 1000u32` на реальную aggregation через `participation_history: Vec<u32>` per τ₂ window;
2.**(LOW)** оптимизировать double-sign pattern: вычислить `apply_proposal` first, затем сразу подписать header с post_state_root (одна signature вместо двух);
3.**(LOW)** переименовать `IDENTITY_MAGIC = b"mt-local"` на `b"montana1"` либо `b"mtid001"` для устранения naming clash потенциала;
4.**(INFO)** обновить SPEC_DEVIATIONS.md DEV-001…DEV-009 со статуса «commit pending» на «closed (commit <sha>)».
### 9.6 Оценка узла: **8.0 / 10**
Singleton-mode работает корректно через canonical pipeline. Снижение на 1.0 за placeholder поля `ProposalHeader` (control_root, target, single_leaf_root, median_permille) — известные artefacts singleton, требующие proper закрытия при переходе к multi-node M6+.
---
## 10. SSOT cross-check
Я проверил соответствие spec ↔ code для всех значимых SSOT-ограниченных сущностей.
### 10.1 Размеры криптопримитивов (one source: `mt-crypto/src/lib.rs:71-81`)
| Константа | Spec значение | Code значение | Файл |
**32 domain separator** в `mod domain` — соответствует AUDIT.md заявлению. Префикс `mt-` для всех. Все consensus-critical hash-композиции в production коде используют `domain::*` импорт. Я проверил литеральные `b"mt-..."` подстроки за пределами `mt-codec`:
- **Все вне-mt-codec literal `b"mt-..."`** находятся в `#[cfg(test)] mod tests` блоках для проверки expected hash значений (legitimate test setup);
- **Один acceptable исключение**: `IDENTITY_MAGIC = b"mt-local"` в `montana-node/identity.rs:17` — file format magic, не domain separator (см. L-NODE-1 finding).
| `admission_divisor` | 130 (M4-LOW-7 closure: ранее hardcoded const, теперь в ProtocolParams) | [mt-genesis/src/lib.rs:95](Code/crates/mt-genesis/src/lib.rs) |
VERSION.md заявляет spec target = `Montana v35.3.2 (2026-04-28)`. AUDIT.md ссылается на `Montana v34.0.0 (2026-04-27)` — **AUDIT.md устарел на три минорных bump'а** (v34.0.0 → v35.2.0 → v35.3.0 → v35.3.1 → v35.3.2 за один день после AUDIT.md). Это **не код-блокер**, но AUDIT.md документ требует обновления для consistency.
Все тесты прогнаны мной с настройками `[build] jobs = 1` + `RUST_TEST_THREADS = 1` (зашиты в `.cargo/config.toml` для предотвращения перегрева MacBook на PBKDF2 c=2²⁰).
2.**`panic = "abort"` в release профиле** ([Cargo.toml:21](Code/Cargo.toml)) — корректно для consensus-критичного бинарника. Никакого unwinding с partially-modified state.
3.**`overflow-checks = true` в обоих dev и release профилях** ([Cargo.toml:22, 25](Code/Cargo.toml)) — предотвращает silent wrap. Все consensus-арифметика дополнительно использует `checked_*` либо `saturating_*` per [I-9].
4.**Reproducible release builds** через Docker container с pinned base image (`debian:bookworm-slim@sha256:40b107342c492725bc7aacbe93a49945445191ae364184a6d24fedb28172f6f7`) ([docker/release-build.dockerfile](Code/docker/release-build.dockerfile)) — CI gate `reproducible_release` в .github/workflows/ci.yml.
### Блокеры mainnet deployment (НЕ блокеры аудита кода)
- **B-1**: Genesis ceremony pending — `bootstrap_account_pubkey`, `bootstrap_node_pubkey`, `target_zero`, `genesis_content_data_hash` всё ещё placeholder `[0u8; N]`. Programmatic detection через `is_genesis_bootstrap_finalized()`. Требует multi-party ceremony либо single trusted party — design decision автора. (**знал, документировано в AUDIT.md и SPEC_DEVIATIONS DEV-010**)
### Medium
- **M-M4-1** (LOW→MEDIUM при scaling): `BundleError::TooManyOps`/`TooManyReveals` cap = 65 535 (u16 length prefix). На 1B пользователях с 1000 узлами — 100K+ op_hashes/окно. M6+ spec-patch на u32 length prefix.
- **M-NODE-1**: hardcoded `median_permille = 1000u32` для singleton; replace на real aggregation в M6+.
- **INFO-M3-2**: settle_window отделён от apply_proposal через documentation, не typestate.
- **INFO-M4-4**: `compute_control_set` filter inclusive границы — M6+ network-layer concern для reorg/resync.
- **INFO-M4-5**: magic number 69 в `seniority_bonus = chain_length / 69` — обоснование в спецификации (не проверено в этом аудите по принципу нулевого доверия к доке).
- **INFO-NODE-5**: `target: u128::MAX` placeholder в singleton mode.
### Документация / housekeeping
- **DOC-1**: `AUDIT.md` ссылается на spec target v34.0.0; актуальный target по `VERSION.md` = v35.3.2. Обновить.
- **DOC-2**: `SPEC_DEVIATIONS.md` DEV-001…DEV-009 — статус «commit pending» устарел; реальные commits сделаны (`546a866`, `0921db6`, `db01de6` и др.). Обновить на «закрыто (commit <sha>)».
---
## 14. Сильные стороны проекта в целом
1.**Полная type-level дисциплина**: 0 unsafe без SAFETY, 0 panic в production paths без protocol-invariant обоснования, 0 HashMap/HashSet, 0 f32/f64, 0 SystemTime/Instant в consensus path, 0 thread_rng / OsRng в lib коде.
2.**Domain separation первого класса**: 32 SSOT registry в `mt-codec`, NUL-byte separator `domain ‖ 0x00 ‖ parts...` (P1 closure).
3.**Secret hygiene**: heap+mlock+zeroize+`!Clone`+`!PartialEq` — не worse чем libsodium / boringssl.
4.**Integer arithmetic per [I-9]**: ВСЕ consensus formulas в integer Q-format с binding test vectors. `log2_q64` — degree-3 Remez minimax с hardcoded coefficients.
5.**`[I-8]` Network-Bound Unpredictability**: `cemented_bundle_aggregate` корректно исключает signatures и op_hashes из input; canonical sort по `node_id` обеспечивает order-independence.
10.**Производственная дисциплина**: production-grade naming везде ([C-12]), zero deferred policy в audit-ready состоянии ([C-6]), `panic="abort"` + `overflow-checks=true`.
---
## 15. Итоговая оценка: **8.7 / 10**
Аудит подтверждает: M1…M5 готовы к engagement производственной security firm (NCC Group, Trail of Bits, Quarkslab, Cure53). Открытых блокеров кода — нет. Block-листы только относятся к (а) Genesis ceremony перед mainnet, (б) singleton-mode placeholder полям при переходе к multi-node M6+, (в) обновлению AUDIT.md / SPEC_DEVIATIONS.md документации.
**Распределение оценки:**
- M1: 9.0/10 — excellent crypto layer
- M2: 9.0/10 — solid foundation
- M3: 9.0/10 — clean apply layer
- M4: 8.5/10 — minor scale concern (u16 length prefix)
- M5: 9.0/10 — minimal correct persistence
- montana-node integration: 8.0/10 — singleton placeholders для M6+
2.**(LOW)** обновить устаревшие комментарии `mt-state/src/lib.rs:56,92` на актуальные размеры структур (2098B / 2082B).
3.**(MEDIUM)** при переходе к multi-node M6+ заменить hardcoded `median_permille = 1000u32` на real participation aggregation; запланировать spec-patch на u32 length prefix для bundle hashes.
---
## Приложение A — методология восстановления отчёта
**Конец отчёта.** Все файлы в `Протокол/` помимо кода рассматривались как сырой target scope, не как источник истины. Все findings сопровождены конкретными ссылками на `crates/<crate>/src/<file>:<line>`.