feat(publisher): admit cathedral_voice_hybrid with receipt gates - #118
bateesatobi wants to merge 3 commits into
Conversation
Allow Cathedral Voice hybrid scores on POST /v1/external-scores/violet with complete snapshots, dedicated HMAC/bearer, fraction-required capped blend, and fail-closed cathedral_voice_receipt_v1 / honest-GPU / TDX simulation gates. Co-authored-by: Cursor <cursoragent@cursor.com>
|
Sorry for the slow response on this one. Read it through. The shape is right: adding Two things before this lands. 1. You added the source to the other three fail-closed sets, but AUDIENCE_REQUIRED_SOURCES = {"cathedral_confidential_tdx"}The consequence is at expected = configured_score_audience(source)
if expected is not None:
...enforce (network, netuid) == expected, else score_audience_mismatch...
return network, netuid
# falls through to the permissive branch, where network/netuid are optional
Counter-argument, so this is weighted honestly: the HMAC secret is per source ( 2. No CI has run on this branch. Separately, the two deploy items in your test plan are still unticked. Is the intent to land this ahead of the token and HMAC secret provisioning, or should it wait until one real hybrid report has gone through end to end and returned 202? I am fine either way, I just do not want it merged and then sitting half-provisioned, because an admitted-but-unprovisioned source fails at 503 rather than anywhere obvious. |
HMAC isolation is not enough: without AUDIENCE_REQUIRED_SOURCES the report can omit (network, netuid) and be accepted on a publisher bound to a different subnet. Match cathedral_confidential_tdx fail-closed posture. Co-authored-by: Cursor <cursoragent@cursor.com>
|
Not an omission I intended to keep — you are right, and it is now a one-line join of HMAC-per-source is defense in depth, not a substitute for binding. Without the audience set, Follow-up on this branch (
Deploy / merge intent: land the code after CI is green, but do not enable ingest for this source until dedicated token + HMAC are provisioned and one real hybrid report has returned 202. An admitted-but-unprovisioned source failing 503 is worse than waiting. Unticked deploy items stay unticked until that E2E 202. |
Resolve conflicts against the new publisher-wide audience bind, evidence gate, and fraction-exempt rail. Keep hybrid on complete/HMAC/no-primary and fail-closed receipts. Co-authored-by: Cursor <cursoragent@cursor.com>
wallscaler
left a comment
There was a problem hiding this comment.
Artifact / Question / Out of scope
- Artifact: PR #118 "feat(publisher): admit cathedral_voice_hybrid with receipt gates" at head
70fed14(forkbateesatobi/cathedral-validator-voice). Three commits:09e2739(feature),393d23d(audience bind),70fed14(merge of main at4234d5b). Five files, +504/-7. All file:line cites are to the head unless marked(main). - Question: Did
393d23dland as described. Were the earlier review points addressed. Fresh pass on: (a) server-side vs client-asserted gates, (b) composition and cap interaction with the confidential source, (c) simulation flag defaults across deploy paths, (d) rebase risk against current main. - Out of scope: the Cathedral Voice producer itself, the receipt format's cryptographic design, the cybergym lane, pre-existing macOS-only test failures, the retired relay installer.
Status of the earlier review points
AUDIENCE_REQUIRED_SOURCESnot extended. Addressed, but not by the line the author described.393d23dadded the source to the set. The merge70fed14then took main at4234d5b, where #136 (47c3525) had already deleted the set in favor of a publisher-wide bind. At head the set does not exist. Every source goes through_report_audience(scaffold/publisher/external_scores.py:136-146, called at:432). The three tests the author named exist atscaffold/publisher/tests/test_voice_hybrid.py:92-121and pass (16/16 on head and on main+PR). Net effect matches the confidential posture. The author's comment describes393d23d, not the head.- No CI on the branch. Not addressed, and the blocker is on the maintainer side. The PR is cross-repository. Both workflow runs (
09e2739,70fed14) sit inaction_required, waiting for a maintainer to approve a fork run.gh pr checksshows nothing because no check-run was ever created. Approve the run or push the branch into the org repo. - Deploy items and merge intent. Answered: land code after green CI, do not enable ingest until token and HMAC are provisioned and one real 202 is seen. See Finding 2: under the production profile, "enable later" is not an env change.
Verdict
Changes requested. Merging the head onto current main turns three gating tests red (tests/thin/test_operator_docs_safety.py x2, tests/boundary/test_canonical_validator_authority.py x1) because the PR restores docs/VIOLET_EXTERNAL_SCORES.md, which main retired to an eight-line # Retired stub in 293e554. GitHub already reports the PR as CONFLICTING (mergeStateStatus: DIRTY) on that one file.
Findings
F1. The docs hunk breaks the gating lanes after rebase (merge-blocker).
- Code path: PR diff to
docs/VIOLET_EXTERNAL_SCORES.md. On main the file is an eight-line retired pointer.tests/thin/test_operator_docs_safety.py:333-343asserts it starts with# Retired;:290assertscathedral-validator serveis absent from active docs;tests/boundary/test_canonical_validator_authority.pyasserts the same for retired console commands. - Behavior: the Python 3.11 and 3.12 lanes (tests/thin, tests/boundary) go red the moment this branch is rebased or merged. Verified: merged tree 24 failed vs main 21 failed in the same venv; the three extra are exactly these tests.
- Fix: drop the docs file from the PR. If the voice-hybrid section is needed, it belongs in
docs/PUBLISHER_CONFIGURATION.md, the live page.
F2. Composition is unreachable under the production profile, and the PR does not say so.
- Code path:
scaffold/publisher/launch_profile.py:191-192 (main)pinsCATHEDRAL_EXTERNAL_SCORES_MODE=confidential_primaryandCATHEDRAL_EXTERNAL_SCORES_SOURCE=cathedral_confidential_tdxunderv2-converged. The shipped unit selects production (deploy/publisher/cathedral-scorer-sn39.service:21) and the env example selects that profile (deploy/publisher/cathedral-scorer-sn39.env.example:9).scaffold/publisher/weights.py:1297-1333degradesconfidential_primaryto burn when the source is not inCONFIDENTIAL_PRIMARY_SOURCES; voice hybrid is excluded by design (:150). - Behavior: in production a voice hybrid report is admitted and stored (ingest is source-agnostic) but never composed. The blend path the PR configures (
weights.py:1387onward) is dead under the only profile production boots. The docs recipe (MODE=blend,FRACTION=0.1) describes the compatibility profile, which production refuses to start. - Relevance: the author's plan ("enable ingest later after E2E 202") assumes a config flip. Enabling this lane needs a new named launch profile and a decision on what
confidential_primarymeans once a second source exists. Neither is in this PR.
F3. The receipt gates are shape checks on client-asserted fields; nothing is verified server-side.
- Code path:
scaffold/publisher/voice_hybrid.py:116acceptsmetadata.receipt_verifiedas a client boolean.:76-81checkrequest_hash,audio_content_hash,signaturefor non-empty only.:100-103checkmrtdandquotefor non-empty only.:96-99readdebugandsimulatedas client booleans.:84-87read the GPU flags likewise. No signature is verified against any key, there is no mrtd allowlist, no quote is parsed. - Behavior: the only control between a POST and stored positive scores is the dedicated bearer plus HMAC (
scaffold/publisher/app.py:3522-3533 (main)). A producer holding the HMAC secret that sendssimulated: falseandgpu_attested: falseis admitted regardless of what ran. The "reject simulated TDX" gate stops an honest producer that labels itself, not a dishonest or compromised one. Economic exposure is the full configured external fraction, bounded byMAX_FRACTION(default 0.5,weights.py:1121), not by the 10% hard cap. - Relevance: this is the PR's headline control ("fail-closed admission", "receipt gates"). It matches the
cathedral_confidential_tdxposture (no publisher-side attestation check for that source either), so it is not a regression. The PR body and docs describe it as more than it is. - Ask, either: (i) pin an expected mrtd via env and verify
receipt.signatureagainst a configured Cathedral Voice public key, or (ii) reword PR body and docs to "schema-gated; the per-source HMAC secret is the trust anchor".
F4. Two new bypass flags are not registered with the production denylist.
- Code path:
voice_hybrid.py:34-40addsCATHEDRAL_VOICE_HYBRID_ALLOW_SIMULATION(default off) andCATHEDRAL_VOICE_HYBRID_REQUIRE_TDX(default on).launch_profile.py:145-172 (main)is_PRODUCTION_FORBIDDEN_TRUTHY, enforced at:1077-1088, pinned byscaffold/publisher/tests/test_strict_production_config.py:1000. Neither new flag is listed. - Behavior:
CATHEDRAL_VOICE_HYBRID_ALLOW_SIMULATION=1orCATHEDRAL_VOICE_HYBRID_REQUIRE_TDX=0boots cleanly in production. A copied dev env file silently disables the one gate the PR calls production-critical. - Answer to (c): default is off in code, and no deploy path sets it (grep over
deploy/, both service units, the env example: zero references). The profile does not defend it. - Fix: add
ALLOW_SIMULATIONto_PRODUCTION_FORBIDDEN_TRUTHYand toRETIRED_PRODUCTION_FLAGSin the test; pinREQUIRE_TDXtotruein_PRODUCTION_PINNED_VALUES.
F5. The audience fence suite fails for the new source.
- Code path:
scaffold/publisher/tests/test_external_scores_audience_fence.py:56-178parametrizes overALLOWED_ENDPOINT_SOURCESwith a minimal report (:31-40: complete, one score, no metadata, no receipt). Voice hybrid rejects it withreceipt_verified_required(external_scores.py:476). - Behavior: three failures on the merged tree that do not exist on main (
test_foreign_audience_row_cannot_move_the_local_fence,test_unaudienced_row_cannot_move_the_local_fence,test_epoch_fence_lock_and_gate_serialise_the_same_audience, each[cathedral_voice_hybrid]). The publisher directory is an advisory lane in CI (.github/workflows/tests.yml:224,continue-on-error), so it will not block, but the new source is then never exercised by the fence tests. - Fix: have the test's
_reportbuilder attach hybrid metadata and a receipt for that source, or expose a minimal-valid-report helper fromvoice_hybrid.py.
Refuted
- (b) Two sources exceed policy together. Refuted.
external_scores_source()(weights.py:961) selects one source;_apply_external_scores(:1427) and_apply_confidential_primary(:1297) both read it; cybergym has its own fraction.latest_snapshot_scoresfilters by source and audience. Voice hybrid and confidential TDX cannot compose in the same vector. What is true: voice hybrid's blend share is capped only byMAX_FRACTION, andMAX_FRACTION=1allows 100% in blend mode even withexternal_primaryblocked. That is the PR's stated design and unreachable in production (F2), so not a finding. - The audience fix did not land. Refuted as a defect. The named set is gone; the behavior is enforced and tested.
- Head does not apply on main. Refuted for code.
external_scores.pyon main is byte-identical to the merge-base (empty diff4234d5b..main).weights.pyauto-merges.normalize_report,bearer_authorized_for_source,verify_hmac_for_source,external_scores_mode,_external_blend_weightsall exist under the same names. Only the docs file conflicts. - Simulation flag enabled somewhere in deploy. Refuted. No reference outside
voice_hybrid.py, its tests, and the PR docs. - Zero-score rows without receipt are a credit hole. Refuted. Score 0 rows are stored as 0 and
latest_snapshot_scorestreats 0 as revoke. No credit path. - Local failures are PR regressions. Refuted for 22 of 25 publisher-suite failures and 21 of 24 gating-lane failures. Identical on main-only in the same venv. The remainder are F1 and F5.
Observations
voice_hybrid.py:109-110is dead:status != "ok"already raised at:72. The docstring at:108("zero scores may still carry receipts (revoke)") is false; a receipt with statusrevokedis rejected asreceipt_not_ok.validate_report_metadataruns once per report (external_scores.py:476) and again per row (voice_hybrid.py:133). Harmless.measurement.formatis optional (voice_hybrid.py:94,if fmt and ...); a missing format passes.measurement.hotkeyis optional (:105). Both moot given F3.receiptis persisted for every source.external_scores.py:501readsraw["receipt"]for all sources;:526-527stores it into the normalized entry, which feedsreport_jsonand thereport_sha256digest. Any existing producer that sends areceiptkey gets unvalidated data persisted and a different digest than before, which is the epoch fence and idempotent-retry key. No producer is known to send that key. Gate:526-527on the hybrid source.- Every commit carries
Co-authored-by: Cursorand the PR body a "Made with Cursor" line. Repo convention is no AI attribution. Squash-merge with a clean message. - 22 publisher-suite and 21 gating-lane failures are pre-existing on main in this venv (macOS-only set plus
score_audience_not_configuredeven with the env exported). Not investigated; CI is Linux.
Questions for the author
- What is the intended production path: a new launch profile (for example
v2-converged-voice) or a change toconfidential_primarysemantics? Neither is in this PR; without one the composition is unreachable. - Who holds the Cathedral Voice receipt-signing key, and is there a public key the publisher can verify against? If yes, F3 is small. If no, say so in the docs.
- Is any existing producer sending a
receiptkey on rows today? - Please: rebase onto main, drop the docs file change (or move the section to
docs/PUBLISHER_CONFIGURATION.md), add both flags to the production denylist, fix the fence-test parametrization, then I will approve the workflow run.
Verification record
- Head
70fed14fromrefs/pull/118/head. Merge-base with main4234d5b. Main is 185 commits ahead; 6 touchscaffold/publisher, none touchexternal_scores.py. - Local, one-off, not CI: Python 3.11 venv, fastapi 0.136.3, bittensor 10.5.0, macOS.
| Run | main only | main + PR |
|---|---|---|
test_voice_hybrid.py |
n/a | 16 passed |
| publisher external-score suites, CI env | 22 failed / 84 passed (without strict config) | 25 failed / 441 passed (with strict config) |
tests/thin + tests/boundary minus integration extras |
21 failed / 2521 passed | 24 failed / 2518 passed |
- The three extra publisher failures are F5. The three extra gating failures are F1. Main-only has one failure absent from the merged run (
test_actionable_chain_failures, errno test, flaky). - GitHub:
mergeable: CONFLICTING,mergeStateStatus: DIRTY; workflow runs for both PR shas inaction_required.
Summary
cathedral_voice_hybridonPOST /v1/external-scores/violet(was rejected asinvalid_source_for_violet_endpoint).complete=true, dedicated bearer + mandatory HMAC,metadata.receipt_verified, per-rowcathedral_voice_receipt_v1on positive scores, honest GPU flags, reject simulated TDX unlessCATHEDRAL_VOICE_HYBRID_ALLOW_SIMULATION=1.external_primary(same posture as confidential completeness/auth); does not join confidential 10% global cap orconfidential_primary.Test plan
python3.12 -m pytest scaffold/publisher/tests/test_voice_hybrid.py(13 passed)CATHEDRAL_EXTERNAL_SCORES_TOKEN_CATHEDRAL_VOICE_HYBRID+CATHEDRAL_EXTERNAL_SCORES_HMAC_SECRET_CATHEDRAL_VOICE_HYBRIDMade with Cursor