Closes #177#188
Conversation
…o assert it LIVECAP_MODEL_BASE_URL is documented as a #110 dev-run knob (exercise the download-failure fallback) but the env read was NOT #[cfg(debug_assertions)]- gated, so a release binary honored it — the same #161-class "dev-only env escape compiled into release" the #146 doc claims we don't ship. (Bounded: pointer_url stays pinned to the official HF_REPO, so the SHA-256 digest source can't move — hygiene, not integrity — but a shipped binary should expose no dev env knobs.) Gate it like dev_flags.rs / the #64 bleed dump: debug `blob_base_url()` reads the env; release `blob_base_url()` returns `base_url_or_default(None)` (the official repo) with the env read compiled out entirely. The pure `base_url_or_default` helper + its tests are unchanged; SHA-pinning untouched. The now-release-unused `MODEL_BASE_URL_ENV` const carries `#[cfg_attr(not(debug_assertions), allow(dead_code))]` — unused → no string emitted → the release binary carries no `LIVECAP_MODEL_BASE_URL` symbol. Extend the #176 release-invariants CI job's FORBIDDEN list with LIVECAP_MODEL_BASE_URL so a dropped guard is caught permanently. Proofs (local): debug example strings → present (knob live); release example strings → ABSENT; a seeded un-gated release read → PRESENT (gate would FAIL); `cargo test --release --no-run` + release clippy -D warnings clean; model tests 5/5. No new deps; no caption/audio content logged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
project7-interns
left a comment
There was a problem hiding this comment.
Verdict: APPROVE
Epic Alignment: PASS
#177 correctly extends the #176 release-invariants job and removes the release-model-host override while preserving debug fallback behavior.
Checked (evidence)
- Contract: crates/livecap-core/src/model.rs:49-57 compiles the environment read only under debug_assertions; release selects the official base via base_url_or_default(None).
- Boundary: model.rs:169 is the sole blob-download base use; pointer_url remains pinned to HF_REPO.
- Invariant: ci.yml:102 adds LIVECAP_MODEL_BASE_URL to the release-binary forbidden-name assertion.
- Riskiest part: retaining the const in release; it is unused and the live release-invariants job proves its symbol is absent from the real binary.
- Kill-list: scanned full diff — clean.
- CI: gh pr checks 188 → app-macos, color-guard, no-stub-gate, packages-linux, release-invariants all pass.
Findings
None.
Decision
The dev-only override is compiled out of release and permanently covered by the extended release-profile gate.
RE2 — APPROVE at PR #188 head (all 5 CI checks green)The #177 debug-gating of Checked (evidence)
(Note: I could not run a local release strings-check — the scratchpad hit |
Closes #177
Debug-gate
LIVECAP_MODEL_BASE_URLso a release binary always uses the official model repo — it was documented as a dev-only knob but honored in release — and extend therelease-invariantsCI job (created by #176) to assert its absence permanently.EPIC Alignment
Part of EPIC #134 (v1.3 audit follow-up), Batch 45 (standing-order … #176 → #177). #176 created the
release-invariantsjob; this ticket EXTENDS it (the create-then-extend boundary RE2 called out — #176 merged first). #161 sibling. Scoped tocrates/livecap-core/src/model.rs+ therelease-invariantsjob in.github/workflows/ci.yml. SHA-pinning (#176) is untouched; does not touchresample.rs/render.ts/parse.ts/pipeline.rs(reserved for #178/#179), dashboard, or settings.Self-Verification
The bug.
LIVECAP_MODEL_BASE_URLis documented (model.rs:27-30) as a #110 dev-run knob (redirect the model-BLOB download base to exercise the download-failure fallback), but the env read atblob_base_url()was not#[cfg(debug_assertions)]-gated — a release binary honored it. Same class as #161 (a dev-only env escape compiled into release, contradicting the #146 "no dev env path in a shipped binary" claim). Bounded impact:pointer_urlis pinned to the officialHF_REPO, so the expected SHA-256 source can't move with the download source — a hostile base URL can't serve a tampered model (hygiene, not integrity) — but a release build should carry no such knob (and the redirect would leak which model the user runs to an arbitrary host).The fix (mirror the #161 / dev_flags.rs pattern).
blob_base_url()is cfg-split (model.rs):#[cfg(debug_assertions)]readsstd::env::var(MODEL_BASE_URL_ENV);#[cfg(not(debug_assertions))]returnsbase_url_or_default(None)— the official repo, unconditionally, with the env read compiled out entirely.base_url_or_defaulthelper and its tests are unchanged; SHA-pinning ([audit][infra] CI & gates hardening: release-mode job, SHA-pin actions, release.yml quality gates, no-stub-gate + color-guard blind spots, VAD test-note contradiction #176) is untouched.MODEL_BASE_URL_ENVconst gets#[cfg_attr(not(debug_assertions), allow(dead_code))]. An unused const emits no string, so the release binary carries noLIVECAP_MODEL_BASE_URLsymbol — exactly what the strings gate asserts. (In debug it's used → present → the fallback knob works.)release-invariantsCI job'sFORBIDDENlist withLIVECAP_MODEL_BASE_URL, so a future dropped guard is caught permanently on every PR.Security invariants. No new dependencies. No caption/audio content logged (only an env-var name is asserted absent via
strings; the workflowrun:block uses static values). Simplest fix — the #161 cfg pattern, no refactor of the download logic. Onlymodel.rs+ the release-invariants job touched.Tests / evidence (
livecap-corebuilds on Linux; the actuallivecap-apprelease build + strings run in therelease-invariantsCI job on this PR):cargo build --release --example live_caption -p livecap-core(links livecap-core, mirroring how livecap-app links it) →strings … | grep LIVECAP_MODEL_BASE_URLempty.LIVECAP_MODEL_BASE_URL(the Settings: whisper model selection (small / medium / large-v3-turbo) with download-on-switch #110 knob is compiled in and read).cargo test --release -p livecap-core --lib --no-runclean;cargo clippy --release -- -D warningsclean (theallow(dead_code)handles the unused const); debugclippy -D warningsclean.base_url_or_defaultunit tests (override defaults to official repo; override moves only the blob URL, never the digest source) 5/5 pass; ci.yml valid YAML with the FORBIDDEN list extended.Design Fidelity
N/A — no UI/webview/CSS surface changed (a Rust cfg gate + a CI
stringsassertion only).Deviations
MODEL_BASE_URL_ENValways-defined (with#[cfg_attr(not(debug_assertions), allow(dead_code))]) rather than#[cfg(debug_assertions)]-gating the const itself, so the intra-doc link frompointer_url's security note (see [MODEL_BASE_URL_ENV]) stays resolvable in both profiles. An unused const emits no string, so this does not weaken the release-absence guarantee (verified).🤖 Generated with Claude Code