montana/Монтана-Протокол/Код/docs/audit-checklist.md

369 lines
25 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Pre-audit self-attestation checklist — M1+M2+M3+M4+M5+M6+M9 layers
Заполняется архитектором перед каждым external audit engagement. Все пункты должны быть `[x]` либо иметь явное обоснование почему `[ ]`.
---
## A. Conformance proofs
- [x] **NIST FIPS 204 ML-DSA-65 KeyGen byte-exact** vs ACVP-Server published vectors (25 cases)
- [x] **NIST FIPS 203 ML-KEM-768 KeyGen byte-exact** vs ACVP-Server published vectors (25 cases)
- [x] **NIST FIPS 204 ML-DSA-65 SigGen deterministic byte-exact** для empty context (1 case)
- [x] **NIST FIPS 180-4 SHA-256** vector "abc" → ba7816bf...15ad
- [ ] **NIST FIPS 204 ML-DSA-65 SigVer** (deferred — косвенно подтверждено через round-trip; direct NIST KAT не добавлен в M1-F)
- [ ] **NIST FIPS 203 ML-KEM-768 Encapsulate/Decapsulate** (deferred — Montana M1-F scope только KeyGen, encapDecap нужен в M6+)
## B. Code surface
- [x] **Layer 1 Rust shim** **662 строк** (`crates/mt-crypto/src/lib.rs`), все **7** `unsafe` blocks с `// SAFETY:` комментариями: 4 FFI sites (`fn keypair_from_seed`, `fn sign`, `fn verify`, `fn keypair_from_seed_mlkem`) + 3 mlock/munlock (`impl Drop for SecretKey`, `fn alloc_locked_secret_box`, `impl Drop for MlkemSecretKey`). Точные строки сверять через `grep -n "unsafe " crates/mt-crypto/src/lib.rs` (line refs не фиксируем — drift при growth).
- [x] **Layer 2 own C wrapper** **457 строк** (`mt_crypto.c`) + **67 строк** (`mt_crypto.h`), focused EVP API wrapping, `-Wall -Wextra -Wpedantic -Werror`
- [x] **Layer 3 vendored OpenSSL 3.5.5 LTS** через `openssl-src = "=300.5.5+3.5.5"` byte-pinned
- [x] Total own audit surface (Layer 1 + Layer 2) **1280 строк** (662 Rust shim + 49 FFI bindings + 457 C wrapper + 67 C header + 45 build script) — small enough для thorough review. Точные числа: `wc -l crates/mt-crypto/src/lib.rs crates/mt-crypto-native/src/lib.rs crates/mt-crypto-native/csrc/mt_crypto.{c,h} crates/mt-crypto-native/build.rs`.
- [x] No `serde` auto-derive в consensus-critical types (custom `CanonicalEncode` trait)
## C. Memory safety + secret hygiene (Pass 17 enforcement)
- [x] **Drop+zeroize** для `SecretKey` (4032B) и `MlkemSecretKey` (2400B)
- [x] **Heap-allocated через Box** — secret bytes живут в heap, не stack inline (verified `secret_key_is_heap_allocated` test: `size_of::<SecretKey>() == 8` pointer)
- [x] **mlock applied** на heap pages для secret bytes (best-effort через `alloc_locked_secret_box`); fallback assumption: encrypted swap (FileVault macOS / LUKS Linux)
- [x] **Stack hygiene при FFI**`keypair_from_seed*` пишет напрямую в heap-locked Box; никаких stack temporary buffers с secret bytes (verified в [crates/mt-crypto/src/lib.rs](../crates/mt-crypto/src/lib.rs) — функции `keypair_from_seed` и `keypair_from_seed_mlkem`, search by name)
- [x] **munlock в Drop** перед heap dealloc (best-effort, errno ignored)
- [x] No `Clone`/`Copy` derive на secret types (compile-time enforced via security_invariants test)
- [x] No `PartialEq`/`Eq` на secret types (предотвращает timing leak через ==)
- [x] No secret bytes в logs / stdout / stderr (file-content scan через `no_println_or_log_on_secret_bytes_in_lib_code` test)
- [x] `mt-examples/m1_crypto.rs::print_sk` gated через env var (`M1_DUMP_SK=1` opt-in; по умолчанию SK bytes redacted; механизм: `dump_sk_enabled()` функция в [m1_crypto.rs](../crates/mt-examples/examples/m1_crypto.rs#L39-L41) проверяется в `print_sk` строка 76)
- [x] FFI buffer sizes match contract (PUBLIC_KEY_SIZE / SECRET_KEY_SIZE / SIGNATURE_SIZE constants used consistently)
- [x] **13 security invariants automated** в [crates/mt-crypto/tests/security_invariants.rs](../crates/mt-crypto/tests/security_invariants.rs) — regression detection
- [x] **6 Security Cards заполнены** в [docs/security-cards.md](security-cards.md) — Pass 17 mandatory enforcement
## D. Error surface
- [x] **`sign` / `keypair_from_seed` / `keypair_from_seed_mlkem`** возвращают `Result<_, CryptoError>` (не panic)
- [x] **CryptoError enum** с 11 variants (Display + std::error::Error impls)
- [x] No `unwrap()` / `expect()` в lib коде кроме явного internal invariant с комментарием
- [x] No silent error swallowing (`.ok()`, `let _ = ...`)
## E. Determinism (consensus path)
- [x] **FIPS 204 Algorithm 2 deterministic Sign** через `OSSL_SIGNATURE_PARAM_DETERMINISTIC=1`
- [x] No `f32`/`f64` в crypto path (consensus determinism per Montana [I-3] + [I-9])
- [x] No `HashMap`/`HashSet` iteration order dependency
- [x] No `SystemTime::now`/`Instant::now` в consensus path (только в test/tool helper `keypair()` который gated `#[cfg(any(test, feature = "testing"))]`)
## F. Misuse resistance
- [x] **`SecretKey::from_array(arbitrary_bytes)`** при последующем `sign()` возвращает `Err(CryptoError::InvalidSecretKey)`, не panic (F-7 closure через F-2 Result API)
- [x] `keypair()` (weak entropy test helper) **не доступен** в production binary (cfg-gate)
- [x] Public type fields private (no struct literal construction обходящая validation)
- [x] No `Default` impl для types requiring real crypto material
## G. Build & reproducibility
- [x] **`Cargo.lock`** committed
- [x] **Точные версии всех dependencies** (`=X.Y.Z`)
- [x] **`rust-toolchain.toml`** pinned
- [x] **Docker reproducible build** с pinned base image digest
- [x] **CI gate `reproducible_release`** проверяет byte-identity между двумя независимыми runs
- [x] **Cross-compile correctness** через `CARGO_CFG_TARGET_OS` env var
## H. Dependencies
- [x] **Crypto deps** все production-grade (OpenSSL 3.5.5 LTS, sha2 0.10.9, no pre-1.0 в consensus path)
- [x] **No "USE AT YOUR OWN RISK" libraries** в production paths
- [x] **`cargo audit`** clean (verified 2026-04-26: 0 vulnerabilities, 0 warnings, 39 dependencies scanned). Prerequisite: `cargo install cargo-audit --locked` (один раз). Verify: `cd "<repo-root>" && cargo audit`
- [x] **`cargo tree -p mt-crypto | grep -iE "ml-dsa|ml-kem|hybrid-array"`** → 0 hits (RustCrypto pre-1.0 deps удалены полностью per M1-F migration)
- [x] **License compatibility** — все deps MIT / Apache-2.0 / BSD / ISC
## I. Documentation
- [x] **`AUDIT.md`** в корне репозитория с audit chain + threat model + reproduction commands
- [x] **Threat model** explicit: in scope / out of scope / known limitations с closure paths
- [x] **Spec references** в коде через `// spec, раздел "<name>"` без версии (single source of truth — `VERSION.md`)
- [x] **Manual Validation Gate scenarios** documented в `ROADMAP.md`
- [x] **Architect + critic roles** ([CLAUDE.md](../CLAUDE.md), [CRITIC.md](../CRITIC.md)) в репозитории — peer-reviewable methodology
## J. Open findings
- [x] **Zero open audit findings** в M1 foundational layer (per AUDIT.md §5)
- [x] All 7 M1-F audit findings (F-1..F-7) закрыты конструкцией
- [x] All 5 audit-package findings (F-A1..F-A5) закрыты конструкцией
- [x] All 4 M0+M1+M2 critic findings (F-1..F-4 from `b4a00b1` audit) закрыты:
F-1 (mt-recovery-fingerprint domain spec drift) → spec patch v33.1.2 → v33.1.3;
F-2 (VERSION.md stale Implementation field) → updated to M0..M5 closed;
F-3 (false positive снят критиком в самом audit);
F-4 (controlled halts documentation) → этот раздел K ниже
- [x] Manual Validation Gate scenarios 0/1 status: ✅ Ready (зеленая под external audit)
## K. Controlled halts (documented panic sites)
Список всех `panic!`/`assert!` в lib коде Montana с обоснованием. Все они — **controlled halts при protocol-invariant violation**, НЕ attacker-triggered, НЕ silent failures. Auditor должен verify что каждый panic site:
- (a) reachable только при invariant violation от trusted source (Genesis params, frozen const)
- (b) imeет explicit comment с обоснованием
- (c) не открыт для attacker-controlled input
| Site | Файл:строка | Trigger | Обоснование |
|------|-------------|---------|-------------|
| `apply_transfer*` balance underflow | [crates/mt-account/src/lib.rs](../crates/mt-account/src/lib.rs) `fn apply_transfer{,_activation}` | `sender.balance.checked_sub(amount)` returns None | Protocol invariant breach: `validate_transfer*` гарантирует `balance >= amount` ДО apply. Halt = caller вызвал apply без validate (programmer error либо memory corruption). Не attacker-triggered: validate-then-apply pattern enforced. |
| `apply_transfer*` receiver/operator balance overflow | [crates/mt-account/src/lib.rs](../crates/mt-account/src/lib.rs) `fn apply_transfer/apply_emission` | `balance.checked_add(amount)` returns None at u128::MAX | Encoded arithmetic horizon — суммарный баланс per-account достиг u128::MAX (~3.4×10³⁸ nɈ). Не достижим под const emission `EMISSION_moneta = 13 × 10⁹ nɈ` per окно. Documented halt. |
| `apply_*` op_height/account_chain_length overflow | [crates/mt-account/src/lib.rs](../crates/mt-account/src/lib.rs) `fn apply_*` | `u32` field counters достигли u32::MAX (~4.29 млрд operations per account) | Encoded arithmetic horizon: 4.29 млрд operations per account, не достижим в реалистичный срок. Documented halt. |
| `window_w_to_u32` cast overflow | [crates/mt-account/src/lib.rs](../crates/mt-account/src/lib.rs) `fn window_w_to_u32` | `window_w: u64 > u32::MAX` при cast в AccountRecord field | AccountRecord использует u32 для window-полей (encoded size optimization 4B vs 8B). Horizon = ~4.29 млрд окон ≈ 8000 лет at 60 sec/window. Documented halt. |
**Все sites:**
- ✅ Имеют explicit panic message с обоснованием
- ✅ Reachable только при achieved arithmetic horizon либо protocol invariant breach
-Не attacker-triggered
- ✅ Halt = correct behavior per spec — protocol upgrade required либо validate-then-apply violated (programmer error)
`mt-crypto` panics gated через `assert_eq!(r, MT_OK, ...)` уже converted в `Result<_, CryptoError>` в M1-F closure (commit `e1164ad`) — там panic-в-lib больше нет.
`mt-examples` test helpers могут panic через `.expect("...")` — это test scaffolding, вне production audit scope (per critic role Scope §«НЕ входит в scope: mt-examples test helpers»). Production binary вызовы из mt-examples (m1_crypto demo) panicи через `.expect("HKDF-derived seed cannot fail KeyGen")` — internal invariant, не attacker-triggered.
## L0. Test strength augmentations (Pass 22)
**Mutation testing (recommended, не block для audit):**
```
cargo install cargo-mutants --locked
cargo mutants --package mt-lottery --package mt-consensus --package mt-entry --package mt-account --package mt-store
```
Mutation testing вводит синтетические `mutations` (изменения арифметики,
boundary conditions, removed function calls) в production code и проверяет
есть ли test что catches каждый mutation. **Surviving mutations** = weak
tests (могут passing на broken code).
**Текущий статус:** не run автоматически в CI. Recommended pre-mainnet
benchmark: ≥80% mutation kill rate для consensus path (M3-M4 крейты).
**Применимость к external audit:** auditor может запросить mutation
report как evidence test strength. Закрытие требует:
1. Run cargo-mutants
2. Analyze surviving mutations
3. Add test cases или strengthen assertions
Closure cost ~1-2 рабочих дня (run + analyze + augment tests). Не блокер
для current external audit engagement — это test quality improvement
metric, не correctness gap.
## L. M3 Storage Cards (per persistent state table)
Per родительский `Протокол/CLAUDE.md` Storage Card invariant: каждая
persistent state table обязана иметь Storage Card до статуса «closed».
Закрывает Gate 14 ([I-14] state lifecycle) для apply_proposal layer.
### AccountTable Storage Card
```
Таблица: AccountTable (mt_state::AccountTable)
Operation создающая запись: TransferActivation (opcode 0x0A)
Платит creation cost: sender (existing account, sponsor pattern)
Размер записи (bytes): 2059 (ACCOUNT_RECORD_SIZE)
Secondary resources per record: SMT leaf hash 32B + потенциальный merkle path
Cost per record: амount > 0 (sender выбирает) — нет fixed
creation fee, sender отправляет любую
amount получателю который при этом получает
AccountRecord
Cost barrier для anti-spam: НЕ через денежный барьер (нарушило бы
[I-15] time-based scarcity); защита через
cooldown 1 TransferActivation per sender
per τ₂ (см. validate_transfer_activation
spec rule (e) [I-15] cooldown enforcement)
Lifecycle condition: нет explicit removal в M3 scope
(CloseAccount opcode 0x0B — M11 milestone,
pending spec finalization payload format)
Lifecycle threshold: N/A в текущей версии
[I-14] путь: 3 (rate-limit через [I-15] time scarcity);
barriers через time, не money
Existing pruning consistent: yes (нет pruning, по design до M11)
[I-14] compliance status: pending M11 — closure path в ROADMAP §M11
«CloseAccount финализация». Rate-limit
через cooldown сейчас limits attacker
account creation rate; explicit deletion
ждёт spec finalization opcode 0x0B.
Conservation invariant (per-op): Σ delta_balance == 0 для Transfer/
TransferActivation (sender -= amount,
receiver += amount, atomic)
Storage growth invariant per τ₂: ≤ active_chain_length / τ₂ × max_accounts_
per_τ₂ (rate-limited через [I-15])
Storage cap: нет explicit hard cap — relies on
[I-15] time-based + future M11 deletion
```
**Sabotage budget analysis:** атакующий $1M / $100k / $10k без profit motive,
максимизирующий state bytes:
- Stake-protected: TransferActivation требует sender с balance > 0 + cooldown;
атакующий создаёт `N` accounts с initial balance, ждёт τ₂ окон, повторяет
активацию каждого отдельным sponsor. Real cost = TC required для
bootstrapping N senders × cooldown overhead.
- Per-τ₂ limit: 1 activation per existing account → max N accounts на
attacker = `existing_accounts × (windows / τ₂)`. Линейный, не
exponential growth.
- Для bloat 1 GB AccountTable: 1 GB / 2059 B ≈ 487K accounts. При 1
activation per account per τ₂ = 487K окон ≈ 6.7 days (при τ₂ = 20160 ×
60sec). Эта оценка предполагает что attacker уже владеет ≈ 487K sender
accounts — что само по себе требует bootstrapping.
- M11 CloseAccount позволит deletion, что smaller surface для accumulated
bloat но не предотвратит short-term spike.
[I-15] time-based + cooldown — текущий primary mitigation. M11 — secondary
explicit cleanup path после spec finalization.
### NodeTable + CandidatePool Storage Cards
Доступ через mt-state types (NODE_RECORD_SIZE = 2098, CANDIDATE_RECORD_SIZE
= 2082). Lifecycle / cost analysis для этих таблиц — domain mt-entry (M4
audit scope, отдельный milestone).
---
## Reproduction one-liners для аудитора
**NIST KAT cross-implementation conformance proof:**
```
cd "<repo-root>" && cargo test -p mt-crypto-native --test nist_acvp_kat -- --nocapture
```
**Internal correctness baselines:**
```
cd "<repo-root>" && cargo test -p mt-crypto-native -p mt-crypto -p mt-mnemonic
```
**Recovery flow end-to-end:**
```
cd "<repo-root>" && cargo test -p mt-mnemonic --test e2e_recovery -- --nocapture
```
**M2 Determinism invariants (mt-merkle / mt-genesis / mt-state / mt-timechain):**
```
cd "<repo-root>" && cargo test -p mt-merkle -p mt-genesis -p mt-state -p mt-timechain --test determinism_invariants -- --nocapture
```
Ожидание: SMT root determinism, Genesis singleton stability,
state table BTreeMap canonical sort,
VDF + cemented_bundle_aggregate per [I-8].
**M3 Determinism invariants (mt-account):**
```
cd "<repo-root>" && cargo test -p mt-account --test determinism_invariants -- --nocapture
```
Ожидание: Transfer/ChangeKey/Anchor/TransferActivation encoded
sizes, op_hash determinism (R2 invariant), apply_* determinism, validate
rejection patterns, settle_window order independence, genesis state
determinism, reward/supply consistency, apply_proposal determinism,
controlled panic on protocol breach (checked arithmetic).
**M4 Determinism invariants (mt-lottery / mt-consensus / mt-entry):**
```
cd "<repo-root>" && cargo test -p mt-lottery -p mt-consensus -p mt-entry --test determinism_invariants -- --nocapture
```
Ожидание: 32 + 27 + 24 = **83 PASS** — bundle_hash/reveal_hash R2 stability,
compute_endpoint [I-8] binding, log2_q64 / ln_q64 / weighted_ticket_node
monotonicity, determine_winner argmin canonical (M4-1 closure: TooManyOps
validation barrier), proposal_hash R2, canonical_proposer / fallback_proposer
Lookback Leadership cascade, compute_control_set canonical sort, validate_*
acceptance, finalization_status, NodeRegistration R2, candidate_vdf_init
[I-8], selection_slots / selection_sort_key, required_vdf_length Adaptive
VDF, distinct domain separators between three sort_key compositions.
**M5 Determinism invariants (mt-store):**
```
cd "<repo-root>" && cargo test -p mt-store --test determinism_invariants -- --nocapture
```
Ожидание: AccountTable / NodeTable / CandidatePool
save/load roundtrip (root byte-equal), CorruptedLength detection, crash
recovery (meta + verify_consistency), prune_proposals, byte-exact equality
для identical input, BTreeMap canonical sort persistence-stable, full state
cycle (open → populate → save → close → reopen → load), R5 atomic rename
verification (no `<name>.tmp` после save, atomic overwrite).
**M5 Pseudo-fuzz harness (mt-store wire decoders):**
```
cd "<repo-root>" && cargo test -p mt-store --test fuzz_decoders -- --nocapture
```
Ожидание: 5 PASS — `decode_account_record` / `decode_node_record` /
`decode_candidate_record` / `decode_proposal_header` /
`load_meta_last_cemented` прогоняются на 7500+ pseudo-random byte arrays
различных длин (deterministic Xorshift64). Invariant: never panic,
always возвращает Result; valid length → Ok, mismatch → StoreError::CorruptedLength.
Pseudo-fuzz используется вместо libfuzzer-sys / cargo-fuzz из-за nightly
toolchain dependency (workspace pinned на stable). При появлении nightly
target — заменить на coverage-guided fuzzing в crates/mt-store/fuzz/
fuzz_targets/.
**M4 External SHA-256 oracle (Pass 25 Independent Oracle):**
```
cd "<repo-root>" && python3 scripts/oracle_python_sha256.py
cd "<repo-root>" && cargo test -p mt-lottery --test external_oracle -p mt-entry --test external_oracle
```
Ожидание (Python): 4 hardcoded hex digests + distinct domains PASS +
input sensitivity PASS.
Ожидание (Rust tests): 4 PASS — `compute_endpoint`, `candidate_vdf_init`,
`selection_sort_key`, `nr_sort_key` byte-exact match Python `hashlib.sha256`
output. Cross-impl conformance verified — независимая reference не от
Rust SHA-256 (sha2 crate) → защита от drift между Rust impl и spec
formula.
**Reproducible release build verification:**
*Prerequisites:* Docker ≥ 20.10 installed and running, bash (для process substitution), ~30 минут wall-clock для двух clean builds, ~5 GB free disk.
```
cd "<repo-root>" && docker build --no-cache --file docker/release-build.dockerfile --tag mt-audit-1 . && docker build --no-cache --file docker/release-build.dockerfile --tag mt-audit-2 . && diff <(docker run --rm mt-audit-1 sha256sum /usr/local/bin/*) <(docker run --rm mt-audit-2 sha256sum /usr/local/bin/*)
```
*Альтернатива без Docker prerequisite:* верифицировать через CI history — каждый push в `main` запускает CI job `reproducible_release` (см. [.github/workflows/ci.yml](../.github/workflows/ci.yml)), который выполняет тот же двойной build с byte-identity assertion. Auditor может проверить green CI runs за период как evidence.
**Build sanity:**
```
cd "<repo-root>" && cargo fmt --all -- --check && cargo clippy --all-targets -- -D warnings && cargo build --all --release
```
---
---
## H. M6 Network layer (mt-net + mt-net-transport)
- [x] **mt-net wire format byte-exact** — ProtocolMessage envelope (14 B header + payload), 18 message types in registry, IBT online + mesh proof, Bootstrap PoW, Uniform Framing (1024 B fixed), 12 structured payloads (FastSync*, PeerList*, BatchLookup*, RangeSubscribe*, Bye)
- [x] **mt-net 110 unit + integration tests pass**`cargo test -p mt-net --features testing`
- [x] **mt-net-transport libp2p TCP+TLS 1.3+Noise+Yamux upgrade chain** — verified through `cargo test -p mt-net-transport --features testing` (14 tests pass)
- [x] **Manual Validation Gate scenario 6 PASS** — two-node handshake e2e (commit `9a15f49`); `tests/e2e_two_node_handshake.rs::two_node_request_response_ping_pong`
- [x] **Manual Validation Gate scenario 7 PASS** — proposal exchange e2e + 512 KiB boundary (commit `04f8d29`); `tests/e2e_proposal_exchange.rs::{proposal_envelope_round_trip, large_payload_near_max_limit}`
- [x] **[C-5] libp2p capability checklist 8/8 PASS** — TCP+TLS 1.3+Noise+Yamux+Swarm primitives, async tokio, rustls + snow constant-time, Linux+macOS+Windows, IPFS+Filecoin+Polkadot 5+ years production, MIT/Apache 2.0
- [x] **Backpressure rules B1-B6 enforced** — max_protocol_payload_bytes (1 MiB) + max_sf_ciphertext_bytes (64 KiB) reject до allocation per spec
- [x] **Critic-fix bundle P-C1..P-C8 closed** — domain registry SSOT в mt-codec, prefix-free rename mt-tunnel→mt-tunnel-online, 5 fuzz harnesses scaffolded, try_new constructors, Bye forward-compat, ibt_mesh_verify O(1) path, no unwrap/expect в lib code
- [x] **5 fuzz harnesses scaffolded** в `crates/mt-net/fuzz/fuzz_targets/` — fuzz_decode_envelope/frame/mesh_frame/sf_envelope/payloads (запуск через `cargo +nightly fuzz run`)
## I. M9 Conformance suite (mt-conformance)
- [x] **mt-conformance crate created** — public binding test vectors для cross-implementation byte-exact verification
- [x] **2 unit tests pass**`envelope_vectors_byte_exact` + `pow_target_byte_exact`
- [x] **Initial vectors covered** — envelope A1/A2/A3 + IBT B1 (after P-C2 rename) + Bootstrap PoW F1/F2 target derivation
- [x] **iOS port мirrored**`iOS/Apps/Montana/MontanaTests/MTConformanceVectors.swift` byte-exact mirror Rust crate
- [ ] **Expansion** (12 TBD-A markers) — defer until app-layer payload format finalization (BatchLookupRequest/Response, RangeSubscribeResponse query/result/blob entry types)
## Sign-off
| Role | Name | Date | Status |
|------|------|------|--------|
| Architect | (architect role per CLAUDE.md v1.15.0) | 2026-05-02 | ✅ Self-attested ready (M6 + M9 added) |
| Critic | (critic role per CRITIC.md v1.7.0) | 2026-05-02 | ✅ All findings closed (8 P-C1..P-C8 + 5 P-S1..P-S5 critic-fix bundle) |
| Author | (project owner) | — | Ожидает решения об audit firm engagement |
| External auditor | (TBD) | — | Pending engagement |
---
**Status:** READY FOR EXTERNAL AUDIT (M1 + M2 + M3 + M4 + M5 + M6 + M9 layers scope, 16 крейтов, ~14640 LOC, 255+ invariants + 14 e2e network tests, 53/53 findings closed: 40 prior + 13 P-C1..P-C8 + P-S1..P-S5 critic-fix bundle; spec v35.23.0 — M6 transport closure + Genesis Decree network params restructure).