Skip to content

Core: доверенный сертификат допускается только к спрошенному региону (V5b2d-5b) - #561

Merged
lemone112 merged 9 commits into
mainfrom
v5b2d-5b-definition-binding
Aug 7, 2026
Merged

Core: доверенный сертификат допускается только к спрошенному региону (V5b2d-5b)#561
lemone112 merged 9 commits into
mainfrom
v5b2d-5b-definition-binding

Conversation

@lemone112

@lemone112 lemone112 commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator

Что это

Доверенный сертификат семейства доказывает, что артефакт цел. Он не доказывает, что артефакт — образ того региона, который спросили. Целый артефакт ДРУГОГО региона проходит и contextual_region, и family_artifact: обе границы отвечают на свой вопрос и ни одна — на этот.

Срез добавляет crates/labcolors-core/src/family_definition_binding.rs — единственное место, где адрес спрошенного определения сверяется с адресом в сертификате:

DefinitionBoundFamilyLoaderV1::load(pipeline, region, certificate, encoded)

asked = ContextualRegionFamilyProviderV1::definition_digest(pipeline, region) против certified = certificate.definition_digest(). Расхождение — типизированный отказ ForeignDefinition { asked, certified }, возвращающий те же owned bytes (без refetch и clone).

Инвариант против рецептов

Таблицы «имя → семейство» нет и не появилось. Модуль сравнивает два content address и не хранит ни одного имени семейства:

  • ноль pub элементов — вся поверхность pub(crate) (6 элементов);
  • единственные строковые литералы во всём модуле — reason у expect(dead_code) и метка debug_struct; ни одного имени семейства;
  • семейство остаётся данными, приходящими извне: сертификат предъявляет потребитель.

Это ровно второе условие открытия слоя из NoPrematureFamilyArtifactApi — ядро владеет механизмом проверки, а не перечнем.

Один путь, а не два источника истины

main принёс FamilyImageCertificateV2::parse_trusted — единственный вход, которым потребитель предъявляет доверенную 254-байтную запись. Срез приведён к нему:

  • обе стороны мержа добавили аксессор definition_digest(); оставлен один (версия main), с указанием, что сверка принадлежит family_definition_binding и является её единственным местом. Текстовый auto-merge молча собрал два одинаковых метода — это поймано и снято;
  • guard получает FamilyImageCertificateV2 только из parse_trusted, своего разбора записи не заводит;
  • фикстура тестов тоже разбирает запись через parse_trusted и пинится равенством с minted-сертификатом, чтобы не стать вторым источником;
  • тестовый мутатор адреса стал когерентным (definition_with_coherent_certificate_for_test: пересчитывает semantic release и receipt), поэтому отказ доказывается на записи, которую parse_trusted принимает, а не на недостижимом значении.

Порядок отказа: чем именно доказан

Чужой регион отвергается до дорогой работы. Доказательство — не рассуждение, а два независимых наблюдения:

  1. Счётчик хеширования payload. FAMILY_ARTIFACT_PAYLOAD_DIGEST_CALLS равен 0 после отказа на валидном (целом) артефакте чужого региона размером 2 MiB. Anti-vacuity в том же тесте: при совпавшем адресе тот же счётчик становится 1 — то есть счётчик действительно наблюдает эту работу.
  2. Отказ вообще не требует байтов. Sweep по всем 32 позициям адреса выполняется с ПУСТЫМ transport (Vec::new()): отказ наступает раньше, чем появляется что-либо, что можно разобрать, захешировать или декодировать.

Обход образа (raw_bitmap24::verify_image) в verify_bound_envelope идёт строго ПОСЛЕ payload_digest, поэтому ноль на счётчике закрывает и его.

Честная оговорка: parse_trusted выполняет два SHA-256 фиксированного размера (над 254-байтной записью и над semantic-тройкой) ещё до guard-а. Это цена получения сертификата вообще, она константна и не зависит от размера артефакта; «дорогая работа», о которой идёт речь, — это 2 MiB payload.

Гейт NoPrematureFamilyArtifactApi

Гейт не сужен. Срез не открывает поверхность (модуль приватный, элементы pub(crate)), поэтому гейт РАСШИРЕН на новый модуль: было 6 записей compile_fail, стало 8. В rustdoc записано, какое из двух условий затронуто: срез двигает второе (полнота механизма), первое (ядро не аутентифицирует запись) им не затрагивается и остаётся невыполненным — слой закрыт целиком.

Проверено отдельно: family_definition_binding отсутствует в разрешённой публичной документации (0 файлов в target/doc/labcolors_core), verify_program_public_surface — PASS, public_items=152.

Мутации: каждый тест убивает мутацию своего инварианта

Все 8 мутаций убиты, выживших нет.

Мутация Убита тестом
M1 guard не срабатывает (if asked != certifiedif false) 4 теста разом: every_byte_…, a_refused_binding_reports_only_its_typed_cause, an_intact_artifact_of_another_region_is_refused_by_type, a_foreign_definition_is_refused_before_any_payload_work
M2 сравниваются только первые 16 байт адреса every_byte_of_the_definition_address_is_compared
M3 отказ перенесён ПОСЛЕ transport-загрузчика a_foreign_definition_is_refused_before_any_payload_work
M4 Debug печатает transport-байты a_refused_binding_reports_only_its_typed_cause
M5 отказ возвращает переаллоцированную копию an_intact_artifact_of_another_region_is_refused_by_type
M6 причина transport схлопнута в фиксированный вариант the_asked_definition_does_not_weaken_the_transport_contract
M7 отвергаемый сертификат перестал быть когерентным every_byte_of_the_definition_address_is_compared
M8 поверхность опубликована (pub mod + pub struct) обе новые записи гейта: NoPrematureFamilyArtifactApi (line 513) и (line 517)

M8 — доказательство того, что две новые записи гейта не вакуумные: при публикации модуля они краснеют.

Аттестации

Мерж трогает crates/labcolors-core/src/lib.rs, поэтому цепь перегенерирована целиком: receipt-v1.jsonreceipt-v1.sha256EXPECTED_SOURCE_CAPSULE_SHA256--emit проверяемого proof.

Перед регенерацией оба верификатора были прогнаны и упали — то есть они действительно наблюдают lib.rs, а не проходят по инерции.

Сверка поле-за-полем с origin/main (плоское сравнение всех ключей):

  • point-support-…-proof-v1.json: изменено ровно 4 поля — source_files[9].sha256 (это lib.rs), source_closure_sha256, verifier_sha256, proof_payload_sha256. Все четыре — дайджесты над исходником;
  • receipt-v1.json: изменено ровно 2 поля — artifacts[6].bytes и artifacts[6].sha256 (это lib.rs);
  • ключей не добавилось и не пропало;
  • ни одно число сертификата не сдвинулось (checks, q55_scale, drop_domain_inclusive, i128_max, anchor_identity_checks и прочие — без изменений).

Итог: PRODUCT_IDENTITY_VERIFIED и PASS.

Проверки (что реально запущено)

Сборка в WSL, toolchain +stable, после cargo clean -p labcolors-core (полная пересборка):

  • cargo test --workspace --locked — зелено, ни одной строки FAILED; labcolors-core 1043 passed / 7 ignored;
  • cargo test -p labcolors-core --locked --doc — 41 passed, включая все 8 записей NoPrematureFamilyArtifactApi;
  • cargo fmt --all --check — OK;
  • cargo clippy --workspace --all-targets --locked -- -D warnings — OK;
  • RUSTDOCFLAGS="-D warnings" cargo doc --workspace --no-deps --locked — OK;
  • verify_clean_set_receipt.py productPRODUCT_IDENTITY_VERIFIED, плюс test_verify_clean_set_receipt.py (31 тест) — OK;
  • verify_point_support_surplus.pyPASS, плюс test_point_support_surplus_source_binding.py (5 тестов) — OK;
  • test_program_public_surface.py + verify_program_public_surface.py — PASS.

Отдельно стоит записать: первый прогон doc-тестов ПОСЛЕ мутационного стенда показал 2 падения именно на новых записях гейта. Это оказался устаревший артефакт сборки от мутации M8, а не регрессия: после cargo clean -p labcolors-core — 41/41 зелено при исходнике с mod (приватный) и pub(crate) struct. Числа выше сняты с чистой пересборки.

Что НЕ доказано и не закрыто

  • Ядро по-прежнему ничего не аутентифицирует. Адрес определения — свободное публично вычислимое поле; самосогласованный сертификат над произвольным образом минтится кем угодно. Срез связывает сертификат с регионом, но НЕ добавляет подпись или якорь доверия. Первое условие гейта остаётся невыполненным, поверхность закрыта.
  • Небезопасный путь остаётся достижимым внутри крейта. FamilyArtifactLoaderV1::load можно вызвать напрямую, минуя guard. Снаружи это недостижимо (весь слой приватен), но принудительного «единственного пути» на уровне типов нет. Cutover, делающий связанный путь единственным, принадлежит срезу публичного семейного шва (v5b2d-5c), а не этому.
  • Мутационное покрытие — целевое, не исчерпывающее. 8 названных мутаций, а не полный мутационный прогон по модулю.
  • Счётчика вызовов декодера на этом пути нет. FAMILY_ARTIFACT_DECODER_CALLS инкрементируется только в fixture-кодеке; путь raw_bitmap24 инструментирован лишь счётчиком хеширования payload. Утверждение о том, что обход образа не выполнялся, опирается на счётчик payload плюс порядок внутри verify_bound_envelope, а не на отдельный счётчик декодера.
  • Независимое review не выполнено — гейт открыт.
  • Удалённые проверки CI на момент открытия PR ещё не отработали.

Откат

Срез изолирован и обратим:

  • откатить целиком — git revert -m 1 <merge-commit> либо просто не мержить PR; ничего вне labcolors-core и цепи аттестаций не затронуто;
  • поведение продукта не меняется: family_definition_binding приватен и не имеет вызывающих в production-путях (expect(dead_code) до cutover), поэтому откат не может сломать потребителя;
  • если потребуется снять только связывание, оставив мерж: удалить mod family_definition_binding; и mod family_definition_binding_tests;, сам модуль, два новых compile_fail в гейте и definition_with_coherent_certificate_for_test, затем обязательно перегенерировать цепь аттестаций (lib.rs меняется) и прогнать оба верификатора.

Summary by CodeRabbit

  • Новые возможности

    • Добавлена проверка соответствия доверенного цветового артефакта запрошенному определению региона до его загрузки и декодирования.
    • Артефакты с несовпадающим определением отклоняются, а исходные транспортные данные сохраняются для диагностики.
    • Ошибки загрузки и повреждённых данных теперь обрабатываются с сохранением существующего транспортного контракта.
  • Исправления

    • Уточнена документация о границах проверки: сопоставление определения не заменяет аутентификацию сертификатов.
  • Проверки

    • Обновлены контрольные хэши и тесты целостности артефактов.

Claude Code added 3 commits August 6, 2026 23:23
An intact artifact passed both existing boundaries while being the image
of a DIFFERENT region: contextual_region only mints the definition
address, and FamilyArtifactLoaderV1 only proves the bytes match the
trusted certificate. Nothing asked whether that certificate is about the
region the consumer requested.

family_definition_binding closes the chain: DefinitionBoundFamilyLoaderV1
takes (pipeline, region, trusted certificate, encoded bytes), compares
the asked definition address with the one the certificate carries, and
returns a typed ForeignDefinition failure — on the record, before the
envelope parse, the payload digest and the image decode. The refusal
returns the same owned bytes, so diagnosis needs no refetch. No
name-to-certificate table enters the core: the family stays external data.

The module registration rebinds the pinned point-support source closure
and the clean-set product receipt without any semantic change.
… (V5b2d-5b)

Mutation testing of the definition-binding slice left three mutants alive.

A partial address comparison survived every test: reducing the guard to
`asked.as_bytes()[0] != certified.as_bytes()[0]` (or to the last 31 bytes)
still refused the one foreign region the suite used, so nothing established
that the address is compared in full. A comparison over a proper part of the
address admits a foreign image that collides on exactly that part.
`every_byte_of_the_definition_address_is_compared` perturbs each of the 32
positions separately and requires an individual refusal; its transport is
empty, which also re-states that the guard refuses before the envelope parse.
`FamilyImageCertificateV2::with_definition_digest_for_test` supplies the
fabricated addresses without re-encoding a 2 MiB artifact per position.

The failure's redacting Debug was untested: making it print the owned
transport left every test green, although the sibling transport boundary
locks that same invariant. `a_refused_binding_reports_only_its_typed_cause`
closes it.

`#[derive(PartialEq, Eq)]` on the failure was dead surface -- no consumer
compares failures by value, and the crate compiles without it.

Kill matrix over the nine mutants (guard inverted, guard disabled, report
swapped, cost before guard, first-byte-only, last-31-bytes, cause flattened,
bytes reallocated, Debug dumping bytes): no survivors.
Обе стороны добавили доступ к адресу определения. Оставлен один:
FamilyImageCertificateV2::definition_digest из main, с указанием, что
сверка принадлежит family_definition_binding и является её единственным
местом.

Guard приведён к единственному входу main: certificate он получает только
из FamilyImageCertificateV2::parse_trusted, своего разбора записи не заводит.
Тестовый мутатор адреса определения стал когерентным
(definition_with_coherent_certificate_for_test): отказ доказывается на
записи, которую parse_trusted принимает, а не на недостижимом значении.

NoPrematureFamilyArtifactApi расширен на family_definition_binding: срез
не открывает поверхность, поэтому гейт не сужается.

Цепь аттестаций перегенерирована под новый lib.rs; относительно origin/main
сдвинулись только дайджесты над исходником, ни одно число сертификата.
@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 31 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: c42599be-1a08-4b1f-bf87-8eeb03d39bff

📥 Commits

Reviewing files that changed from the base of the PR and between 3ead2f4 and 9540ac9.

📒 Files selected for processing (2)
  • crates/labcolors-core/src/family_artifact.rs
  • crates/labcolors-core/src/family_definition_binding_tests.rs

Walkthrough

Добавлен внутренний DefinitionBoundFamilyLoaderV1. Он сравнивает digest запрошенного региона с digest сертификата до загрузки payload. Добавлены типизированные ошибки, тесты, тестовый helper и обновлённые контрольные хэши артефактов.

Changes

Привязка family artifact

Layer / File(s) Summary
Контракт и загрузчик привязки
crates/labcolors-core/src/family_definition_binding.rs, crates/labcolors-core/src/family_artifact.rs, crates/labcolors-core/src/lib.rs
DefinitionBoundFamilyLoaderV1 сравнивает digest определения до вызова FamilyArtifactLoaderV1::load. Ошибки сохраняют причину отказа и исходные encoded bytes.
Тестовые данные и проверка поведения
crates/labcolors-core/src/family_definition_binding_tests.rs, crates/labcolors-core/src/contextual_region_tests.rs, crates/labcolors-core/src/lib.rs
Тесты проверяют успешную загрузку, отказ чужого определения, сравнение всех 32 байт, отсутствие payload-работы до отказа, debug-вывод и сохранение transport-контракта.
Согласованные сертификаты и контрольные хэши
crates/labcolors-core/src/family_artifact.rs, crates/labcolors-core/contracts/..., scripts/verify_point_support_surplus.py
Добавлен helper для coherent certificate. Обновлены receipt, proof и ожидаемый хэш исходного семантического конуса.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

  • Labpics-Team/lab-colors#491: Добавляет связанную функциональность family artifact и certificate.
  • Labpics-Team/lab-colors#494: Вводит contextual-region family definitions и digests, используемые новой привязкой.
  • Labpics-Team/lab-colors#316: Предоставляет базовую инфраструктуру lib.rs, на которую опирается новый модуль.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed Заголовок точно описывает основное изменение: сертификат семейного артефакта допускается только для запрошенного региона.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch v5b2d-5b-definition-binding

Comment @coderabbitai help to get the list of available commands.

The type's rustdoc promises that any failure hands the same owned bytes
back, so a caller never refetches or clones.  Only the foreign-definition
branch proved it: the artifact branch could reallocate and all 1043 tests
stayed green.  Shown by injecting exactly that reallocation — red now, at
the assertion added here.
@lemone112

Copy link
Copy Markdown
Collaborator Author

Независимая проверка нашла выжившего мутанта — закрыт в 3ead2f4b.

Дефект. Rustdoc типа DefinitionBoundFamilyLoadFailureV1 обещает, что при любом отказе возвращаются те же owned-байты, чтобы вызывающему не пришлось перекачивать или клонировать. Доказано это было только на ветке ForeignDefinition. Ветка Artifact могла переаллоцировать буфер — и вся сюита из 1043 тестов оставалась зелёной, потому что тест утверждал только failure.cause().

Проверка чувствительности. Внёс ровно ту переаллокацию, которую описал проверяющий (EncodedFamilyArtifactV2::from_owned_bytes(encoded.into_bytes().to_vec().into_boxed_slice())): test result: FAILED. 5 passed; 1 failed, падение в добавленном утверждении. После восстановления — 6 из 6 зелёных.

Не раскрытое в теле PR. В диффе есть расширение семи элементов contextual_region_tests.rs до pub(crate) — модуль под #[cfg(test)], продовой поверхности не создаёт, но в описании его не было. Называю явно.

Про doc-тесты, для тех кто будет перепроверять. На этом репозитории cargo test --doc даёт ложную регрессию из-за кэша сборки: проверяющий получил 39 passed; 2 failed на дереве, побайтово равном анкору, и 41 passed; 0 failed после cargo clean -p labcolors-core. Числа doc-тестов снимайте только с чистой пересборки.

coderabbitai[bot]
coderabbitai Bot previously requested changes Aug 7, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/labcolors-core/src/family_artifact.rs`:
- Around line 285-304: Вынесите повторяющийся пересчёт когерентности из
definition_with_coherent_certificate_for_test,
image_mismatch_with_coherent_certificate_for_test и
member_count_with_coherent_certificate_for_test в единый приватный #[cfg(test)]
helper. Helper должен последовательно пересчитывать semantic_release, затем
artifact_receipt, а каждый из трёх мутирующих методов должен вызывать его после
изменения соответствующего поля.

In `@crates/labcolors-core/src/family_definition_binding_tests.rs`:
- Around line 162-164: Замените жёстко заданную границу 32 в цикле внутри теста
с bytes на значение, полученное непосредственно из asked или
FamilyDefinitionDigestV2, чтобы проверялась каждая позиция digest независимо от
его текущей ширины.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 1fdbfe7c-c791-46c6-86d7-4787e6017828

📥 Commits

Reviewing files that changed from the base of the PR and between 3ad5091 and 3ead2f4.

📒 Files selected for processing (9)
  • crates/labcolors-core/contracts/clean-set-srgb8-v1/receipt-v1.json
  • crates/labcolors-core/contracts/clean-set-srgb8-v1/receipt-v1.sha256
  • crates/labcolors-core/contracts/point-support-reference-surplus-q55-bps-proof-v1.json
  • crates/labcolors-core/src/contextual_region_tests.rs
  • crates/labcolors-core/src/family_artifact.rs
  • crates/labcolors-core/src/family_definition_binding.rs
  • crates/labcolors-core/src/family_definition_binding_tests.rs
  • crates/labcolors-core/src/lib.rs
  • scripts/verify_point_support_surplus.py

Comment thread crates/labcolors-core/src/family_artifact.rs
Comment thread crates/labcolors-core/src/family_definition_binding_tests.rs Outdated
Claude Code added 2 commits August 7, 2026 17:44
Three test mutators repeated the same two-step block, and the order in it is
mandatory: the receipt covers `semantic_release`, so recomputing the release
second yields a record `parse_trusted` refuses — and a test built on it would
prove a refusal on an input the loader can never see.  A fourth mutator
repeating the block wrongly is exactly the trap; one helper makes it
unrepresentable.  Proven by inverting the order inside the helper: three
tests go red.

The digest-width loop now takes its bound from the value.  With the literal,
a narrower or wider digest would silently leave positions uncompared while
the test stayed green.

Verified: 1049 core tests, fmt and clippy clean.
@lemone112

Copy link
Copy Markdown
Collaborator Author

Оба замечания приняты и закрыты в 0c989be8.

Пересчёт когерентности вынесен в один #[cfg(test)] helper. Ваш аргумент про порядок — точный, и я его проверил исполнением: перестановка внутри helper'а (receipt до release) роняет три теста. Именно эту ошибку четвёртый мутатор мог бы повторить незаметно, и тогда parse_trusted отвергал бы запись, а тест доказывал бы отказ на входе, которого загрузчик никогда не увидит. Теперь ошибка непредставима: место одно.

Граница цикла берётся у самого значения. С литералом смена ширины digest молча оставила бы часть позиций несравнёнными при зелёном тесте.

Прогнано: 1049 тестов ядра, cargo fmt --check и clippy -D warnings чисто.

@lemone112
lemone112 dismissed coderabbitai[bot]’s stale review August 7, 2026 14:51

Оба замечания закрыты в 0c989be: пересчёт когерентности в одном helper (обязательность порядка доказана — перестановка роняет три теста), граница цикла выведена из ширины digest. Ответ в треде PR.

@lemone112
lemone112 merged commit 35253fb into main Aug 7, 2026
10 checks passed
@lemone112
lemone112 deleted the v5b2d-5b-definition-binding branch August 7, 2026 18:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant