field_ios

In-the-field app for Radroots on iOS
git clone https://radroots.dev/git/field_ios.git
Log | Files | Refs | README | LICENSE

commit 6afd777a93293baaa9ab715cfd3686d1ae15a10f
parent 22aafe19bd86c4eefca2cda2ecc0490668114034
Author: triesap <tyson@radroots.org>
Date:   Sat, 19 Sep 2026 19:14:05 +0000

media: enforce visibility before remote retrieval

- Bind media access to the full current query and authoritative visibility
- Recheck visibility after network and file waits before publishing bytes
- Verify wrong scope, retraction, arbitrary URLs and invalid response bytes
- Regenerate exact native artifacts and retain full verification evidence

Diffstat:
MTeraFFI/provenance.json | 54+++++++++++++++++++++++++++---------------------------
MTeraFFI/source.lock | 4++--
MTeraFFI/source/aarch64-apple-darwin.json | 32+++++++++++++++++++++++++-------
MTeraFFI/source/aarch64-apple-ios-sim.json | 32+++++++++++++++++++++++++-------
MTeraFFI/source/aarch64-apple-ios.json | 32+++++++++++++++++++++++++-------
Mcore/crates/tera_core/src/runtime/product_surface/today.rs | 247++++++-------------------------------------------------------------------------
Acore/crates/tera_core/src/runtime/product_surface/today_media_retrieval.rs | 232+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acore/crates/tera_core/src/runtime/product_surface/today_media_visibility.rs | 30++++++++++++++++++++++++++++++
Acore/crates/tera_core/src/runtime/product_surface/today_media_visibility_tests.rs | 337+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mrelease/provenance.json | 4++--
Mtest-fixtures/legacy-identifiers.v1.json | 12++++++++++++
11 files changed, 733 insertions(+), 283 deletions(-)

diff --git a/TeraFFI/provenance.json b/TeraFFI/provenance.json @@ -22,9 +22,9 @@ "sha256": "3ed9b7ece2f86e5b5d1ddc6ecbb97deb49c46ca2ea91a930eaec1b580232345c" }, { - "bytes": 76260296, + "bytes": 76270880, "path": "TeraFFI.xcframework/ios-arm64-simulator/libtera_ffi.a", - "sha256": "559e3ba169e0f7d242428b08043cdfa091acb84c00d7a72c642294e97cfe5ebe" + "sha256": "8b1ec252937d363c716d0ebe21c14632cc0c244e3c51d04513e07587540428f4" }, { "bytes": 80633, @@ -37,9 +37,9 @@ "sha256": "3ed9b7ece2f86e5b5d1ddc6ecbb97deb49c46ca2ea91a930eaec1b580232345c" }, { - "bytes": 76343192, + "bytes": 76351800, "path": "TeraFFI.xcframework/ios-arm64/libtera_ffi.a", - "sha256": "c3cd8151391cd2fbf9f9180e9a6c141b11a5b68ad689ddadb072166767c45fc8" + "sha256": "c4d544e8eb415a9393dcdbca7078ec7a261577c3ffa38e3df500945d683465bb" }, { "bytes": 49312, @@ -77,34 +77,34 @@ "sha256": "3ed9b7ece2f86e5b5d1ddc6ecbb97deb49c46ca2ea91a930eaec1b580232345c" }, { - "bytes": 21861696, + "bytes": 21877840, "path": "native/aarch64-apple-darwin/libtera_ffi.dylib", - "sha256": "1235e1aa48b5244bd156baccffa54da81fea517fbac766fc9da01d93e114049d" + "sha256": "b1264839af1d58a242f305b0d721367ae0768cc90196f4b59d814237de28b3b9" }, { - "bytes": 76260296, + "bytes": 76270880, "path": "native/aarch64-apple-ios-sim/libtera_ffi.a", - "sha256": "559e3ba169e0f7d242428b08043cdfa091acb84c00d7a72c642294e97cfe5ebe" + "sha256": "8b1ec252937d363c716d0ebe21c14632cc0c244e3c51d04513e07587540428f4" }, { - "bytes": 76343192, + "bytes": 76351800, "path": "native/aarch64-apple-ios/libtera_ffi.a", - "sha256": "c3cd8151391cd2fbf9f9180e9a6c141b11a5b68ad689ddadb072166767c45fc8" + "sha256": "c4d544e8eb415a9393dcdbca7078ec7a261577c3ffa38e3df500945d683465bb" }, { - "bytes": 100688, + "bytes": 101576, "path": "source/aarch64-apple-darwin.json", - "sha256": "f97e05d5b587358e94a938b7c74591970a11392cd5a8c87c4d7d12b934629058" + "sha256": "7724f92bcd6c6c339e9b603b1e2104317f677ffe8bc756548dc5d03c1282b371" }, { - "bytes": 100532, + "bytes": 101420, "path": "source/aarch64-apple-ios-sim.json", - "sha256": "6811dca32ae3685b2f7e0121cc2f8e0dfa2401a6826d6ad40b510dd754353677" + "sha256": "d5f50af53363e1d4d6a5b015516b08ba65d0da95c893dfae30dc48b41a5e7ed1" }, { - "bytes": 100528, + "bytes": 101416, "path": "source/aarch64-apple-ios.json", - "sha256": "698ed0982a5d5347d757d7f3dce2ac1f12ea16ad03b92d43f6867e7696b8c73a" + "sha256": "794044e08f464ef25fb388c1f5611513e9fa0fd64d793f83f60adb6af12462f8" } ], "language": "swift", @@ -112,7 +112,7 @@ "schema": "radroots.artifact-manifest.v2", "source": { "repository": "https://github.com/radrootslabs/tera", - "tree": "f1bd4bd0656de8a67a9602889c0655fb3bdadc56" + "tree": "e8bc8e378ae5db8ad84891b9262670a1aaf4e395" }, "source_records": { "aarch64-apple-darwin": "source/aarch64-apple-darwin.json", @@ -139,9 +139,9 @@ "sha256": "3ed9b7ece2f86e5b5d1ddc6ecbb97deb49c46ca2ea91a930eaec1b580232345c" }, { - "bytes": 76260296, + "bytes": 76270880, "path": "Tera/Frameworks/TeraFFI.xcframework/ios-arm64-simulator/libtera_ffi.a", - "sha256": "559e3ba169e0f7d242428b08043cdfa091acb84c00d7a72c642294e97cfe5ebe" + "sha256": "8b1ec252937d363c716d0ebe21c14632cc0c244e3c51d04513e07587540428f4" }, { "bytes": 80633, @@ -154,9 +154,9 @@ "sha256": "3ed9b7ece2f86e5b5d1ddc6ecbb97deb49c46ca2ea91a930eaec1b580232345c" }, { - "bytes": 76343192, + "bytes": 76351800, "path": "Tera/Frameworks/TeraFFI.xcframework/ios-arm64/libtera_ffi.a", - "sha256": "c3cd8151391cd2fbf9f9180e9a6c141b11a5b68ad689ddadb072166767c45fc8" + "sha256": "c4d544e8eb415a9393dcdbca7078ec7a261577c3ffa38e3df500945d683465bb" }, { "bytes": 643709, @@ -174,19 +174,19 @@ "sha256": "7b3f92a57ebcc7873349403243098d5dcf993b2dbdab9ed84fea4a50c20baab0" }, { - "bytes": 100688, + "bytes": 101576, "path": "TeraFFI/source/aarch64-apple-darwin.json", - "sha256": "f97e05d5b587358e94a938b7c74591970a11392cd5a8c87c4d7d12b934629058" + "sha256": "7724f92bcd6c6c339e9b603b1e2104317f677ffe8bc756548dc5d03c1282b371" }, { - "bytes": 100532, + "bytes": 101420, "path": "TeraFFI/source/aarch64-apple-ios-sim.json", - "sha256": "6811dca32ae3685b2f7e0121cc2f8e0dfa2401a6826d6ad40b510dd754353677" + "sha256": "d5f50af53363e1d4d6a5b015516b08ba65d0da95c893dfae30dc48b41a5e7ed1" }, { - "bytes": 100528, + "bytes": 101416, "path": "TeraFFI/source/aarch64-apple-ios.json", - "sha256": "698ed0982a5d5347d757d7f3dce2ac1f12ea16ad03b92d43f6867e7696b8c73a" + "sha256": "794044e08f464ef25fb388c1f5611513e9fa0fd64d793f83f60adb6af12462f8" } ], "schema": "tera.installed-native-artifacts.v1" diff --git a/TeraFFI/source.lock b/TeraFFI/source.lock @@ -1,7 +1,7 @@ schema = "tera.installed-source.v1" repository = "https://github.com/radrootslabs/tera" -source_tree = "f1bd4bd0656de8a67a9602889c0655fb3bdadc56" -manifest_sha256 = "6e9f9645c78446371d5fdfc90007ca89d09ab102cc17f417d03d7eaff08a9810" +source_tree = "e8bc8e378ae5db8ad84891b9262670a1aaf4e395" +manifest_sha256 = "386966b5d6d0f9a1639d26520636d725fe1f035d74f776132c9fb4190e8d6eb7" source_date_epoch = 1787871027 [foundation] diff --git a/TeraFFI/source/aarch64-apple-darwin.json b/TeraFFI/source/aarch64-apple-darwin.json @@ -1173,10 +1173,10 @@ "sha256": "ec18505ac5efb7bfa1b6c351178aa7f6b15d8272d87dc567711acdd65ca372b7" }, "core/crates/tera_core/src/runtime/product_surface/today.rs": { - "bytes": 141339, - "git_blob": "5dfa0eb946df781b7f75fc12fc95b69ee999f255", + "bytes": 133627, + "git_blob": "ad2bc57a5823079b49036f80a17a480fd070f318", "mode": "100644", - "sha256": "a9bd67c9787018560a2a99200e0bcb87714fb4fd62f7423bf146e37659eb7e44" + "sha256": "2eddec5f72e483f09498f8252abf3f558b064e2f80b8ed0aa4594a516a18fe56" }, "core/crates/tera_core/src/runtime/product_surface/today_admission_tests.rs": { "bytes": 13548, @@ -1220,6 +1220,24 @@ "mode": "100644", "sha256": "d5da0dc2e13a5a72731c3efc1b4fd02459acc542a94c5a38380dfa1b9e9520da" }, + "core/crates/tera_core/src/runtime/product_surface/today_media_retrieval.rs": { + "bytes": 8538, + "git_blob": "df5a02041a03f2ccfc9b34ec4e46b77d6f1366a3", + "mode": "100644", + "sha256": "fe2f479a3130c23bfebfa9db38febe79a67383fb7a1dda899c195b43cc83472e" + }, + "core/crates/tera_core/src/runtime/product_surface/today_media_visibility.rs": { + "bytes": 1125, + "git_blob": "70eb543db2055dd67df53c47dee5b6841b787876", + "mode": "100644", + "sha256": "8007c774ae9b9505a0aa632bf51d16606a3846ebf94d74cc3e0b22bb9702f04b" + }, + "core/crates/tera_core/src/runtime/product_surface/today_media_visibility_tests.rs": { + "bytes": 10250, + "git_blob": "ff72b95943c453da26fbfac65ac772bb97045d2e", + "mode": "100644", + "sha256": "7aa4ed5c05dddde1921c2ad601807daecbfb6ba38b4a50ef14ab4994244e1f74" + }, "core/crates/tera_core/src/runtime/product_surface/today_paging.rs": { "bytes": 6207, "git_blob": "0b06f6369cc475cd96625307cdb85a040b7ae923", @@ -2145,13 +2163,13 @@ "sha256": "b052a73a824e8f8b26af2646a4758f13655e04de9551a8271890d3cf2b63209d" }, "test-fixtures/legacy-identifiers.v1.json": { - "bytes": 141516, - "git_blob": "46db8eded2880dd4ba156dd1fbcd655ed0666e0d", + "bytes": 141951, + "git_blob": "9f8976ef731b206d2b7699640d400cfa08f8e9e9", "mode": "100644", - "sha256": "a665ec09cd62714e9227b3849d813b08b07efdc047cd4ea7b714c2b53fbb70ee" + "sha256": "80bdf7c1efc05d83260c08201b8d05ecf2d8780ae477239ec05fddc1bb5024a4" } }, "policy": "staged_inputs", - "tree": "f1bd4bd0656de8a67a9602889c0655fb3bdadc56" + "tree": "e8bc8e378ae5db8ad84891b9262670a1aaf4e395" } } diff --git a/TeraFFI/source/aarch64-apple-ios-sim.json b/TeraFFI/source/aarch64-apple-ios-sim.json @@ -1169,10 +1169,10 @@ "sha256": "ec18505ac5efb7bfa1b6c351178aa7f6b15d8272d87dc567711acdd65ca372b7" }, "core/crates/tera_core/src/runtime/product_surface/today.rs": { - "bytes": 141339, - "git_blob": "5dfa0eb946df781b7f75fc12fc95b69ee999f255", + "bytes": 133627, + "git_blob": "ad2bc57a5823079b49036f80a17a480fd070f318", "mode": "100644", - "sha256": "a9bd67c9787018560a2a99200e0bcb87714fb4fd62f7423bf146e37659eb7e44" + "sha256": "2eddec5f72e483f09498f8252abf3f558b064e2f80b8ed0aa4594a516a18fe56" }, "core/crates/tera_core/src/runtime/product_surface/today_admission_tests.rs": { "bytes": 13548, @@ -1216,6 +1216,24 @@ "mode": "100644", "sha256": "d5da0dc2e13a5a72731c3efc1b4fd02459acc542a94c5a38380dfa1b9e9520da" }, + "core/crates/tera_core/src/runtime/product_surface/today_media_retrieval.rs": { + "bytes": 8538, + "git_blob": "df5a02041a03f2ccfc9b34ec4e46b77d6f1366a3", + "mode": "100644", + "sha256": "fe2f479a3130c23bfebfa9db38febe79a67383fb7a1dda899c195b43cc83472e" + }, + "core/crates/tera_core/src/runtime/product_surface/today_media_visibility.rs": { + "bytes": 1125, + "git_blob": "70eb543db2055dd67df53c47dee5b6841b787876", + "mode": "100644", + "sha256": "8007c774ae9b9505a0aa632bf51d16606a3846ebf94d74cc3e0b22bb9702f04b" + }, + "core/crates/tera_core/src/runtime/product_surface/today_media_visibility_tests.rs": { + "bytes": 10250, + "git_blob": "ff72b95943c453da26fbfac65ac772bb97045d2e", + "mode": "100644", + "sha256": "7aa4ed5c05dddde1921c2ad601807daecbfb6ba38b4a50ef14ab4994244e1f74" + }, "core/crates/tera_core/src/runtime/product_surface/today_paging.rs": { "bytes": 6207, "git_blob": "0b06f6369cc475cd96625307cdb85a040b7ae923", @@ -2141,13 +2159,13 @@ "sha256": "b052a73a824e8f8b26af2646a4758f13655e04de9551a8271890d3cf2b63209d" }, "test-fixtures/legacy-identifiers.v1.json": { - "bytes": 141516, - "git_blob": "46db8eded2880dd4ba156dd1fbcd655ed0666e0d", + "bytes": 141951, + "git_blob": "9f8976ef731b206d2b7699640d400cfa08f8e9e9", "mode": "100644", - "sha256": "a665ec09cd62714e9227b3849d813b08b07efdc047cd4ea7b714c2b53fbb70ee" + "sha256": "80bdf7c1efc05d83260c08201b8d05ecf2d8780ae477239ec05fddc1bb5024a4" } }, "policy": "staged_inputs", - "tree": "f1bd4bd0656de8a67a9602889c0655fb3bdadc56" + "tree": "e8bc8e378ae5db8ad84891b9262670a1aaf4e395" } } diff --git a/TeraFFI/source/aarch64-apple-ios.json b/TeraFFI/source/aarch64-apple-ios.json @@ -1169,10 +1169,10 @@ "sha256": "ec18505ac5efb7bfa1b6c351178aa7f6b15d8272d87dc567711acdd65ca372b7" }, "core/crates/tera_core/src/runtime/product_surface/today.rs": { - "bytes": 141339, - "git_blob": "5dfa0eb946df781b7f75fc12fc95b69ee999f255", + "bytes": 133627, + "git_blob": "ad2bc57a5823079b49036f80a17a480fd070f318", "mode": "100644", - "sha256": "a9bd67c9787018560a2a99200e0bcb87714fb4fd62f7423bf146e37659eb7e44" + "sha256": "2eddec5f72e483f09498f8252abf3f558b064e2f80b8ed0aa4594a516a18fe56" }, "core/crates/tera_core/src/runtime/product_surface/today_admission_tests.rs": { "bytes": 13548, @@ -1216,6 +1216,24 @@ "mode": "100644", "sha256": "d5da0dc2e13a5a72731c3efc1b4fd02459acc542a94c5a38380dfa1b9e9520da" }, + "core/crates/tera_core/src/runtime/product_surface/today_media_retrieval.rs": { + "bytes": 8538, + "git_blob": "df5a02041a03f2ccfc9b34ec4e46b77d6f1366a3", + "mode": "100644", + "sha256": "fe2f479a3130c23bfebfa9db38febe79a67383fb7a1dda899c195b43cc83472e" + }, + "core/crates/tera_core/src/runtime/product_surface/today_media_visibility.rs": { + "bytes": 1125, + "git_blob": "70eb543db2055dd67df53c47dee5b6841b787876", + "mode": "100644", + "sha256": "8007c774ae9b9505a0aa632bf51d16606a3846ebf94d74cc3e0b22bb9702f04b" + }, + "core/crates/tera_core/src/runtime/product_surface/today_media_visibility_tests.rs": { + "bytes": 10250, + "git_blob": "ff72b95943c453da26fbfac65ac772bb97045d2e", + "mode": "100644", + "sha256": "7aa4ed5c05dddde1921c2ad601807daecbfb6ba38b4a50ef14ab4994244e1f74" + }, "core/crates/tera_core/src/runtime/product_surface/today_paging.rs": { "bytes": 6207, "git_blob": "0b06f6369cc475cd96625307cdb85a040b7ae923", @@ -2141,13 +2159,13 @@ "sha256": "b052a73a824e8f8b26af2646a4758f13655e04de9551a8271890d3cf2b63209d" }, "test-fixtures/legacy-identifiers.v1.json": { - "bytes": 141516, - "git_blob": "46db8eded2880dd4ba156dd1fbcd655ed0666e0d", + "bytes": 141951, + "git_blob": "9f8976ef731b206d2b7699640d400cfa08f8e9e9", "mode": "100644", - "sha256": "a665ec09cd62714e9227b3849d813b08b07efdc047cd4ea7b714c2b53fbb70ee" + "sha256": "80bdf7c1efc05d83260c08201b8d05ecf2d8780ae477239ec05fddc1bb5024a4" } }, "policy": "staged_inputs", - "tree": "f1bd4bd0656de8a67a9602889c0655fb3bdadc56" + "tree": "e8bc8e378ae5db8ad84891b9262670a1aaf4e395" } } diff --git a/core/crates/tera_core/src/runtime/product_surface/today.rs b/core/crates/tera_core/src/runtime/product_surface/today.rs @@ -90,6 +90,13 @@ pub use reconciliation::TodayReconciliation; #[path = "today_submission_overlay.rs"] mod submission_overlay; +#[cfg(feature = "mobile-social")] +#[path = "today_media_retrieval.rs"] +mod media_retrieval; + +#[path = "today_media_visibility.rs"] +mod media_visibility; + #[cfg(test)] #[path = "today_reconciliation_tests.rs"] mod reconciliation_tests; @@ -555,14 +562,13 @@ impl TeraRuntime { pending: Phase1InboundMediaPending, ) -> Result<bool, TodayError> { let _command = self.lifecycle.enter()?; + let _projection = self.today_projection_lock.lock().await; let storage = self .client .storage() .map_err(|_| TodayError::RuntimeUnavailable)?; let generation = projection_generation()?; - let mut state = load_state(storage, context, generation) - .await? - .ok_or(TodayError::ProjectionMissing)?; + let mut state = media_visibility::current_state(self, context).await?; let mut trial = state.clone(); let prior_configuration = trial.media_cache.status()?.configuration; if prior_configuration.is_some_and(|value| value != pending.configuration()) { @@ -589,14 +595,13 @@ impl TeraRuntime { failure: Phase1InboundMediaFailure, ) -> Result<bool, TodayError> { let _command = self.lifecycle.enter()?; + let _projection = self.today_projection_lock.lock().await; let storage = self .client .storage() .map_err(|_| TodayError::RuntimeUnavailable)?; let generation = projection_generation()?; - let mut state = load_state(storage, context, generation) - .await? - .ok_or(TodayError::ProjectionMissing)?; + let mut state = media_visibility::current_state(self, context).await?; let changed = mutate_matching_media(&mut state, reference_fingerprint, |media| { media.fail(failure.clone()) })?; @@ -618,14 +623,13 @@ impl TeraRuntime { policy: Phase1MediaCachePolicy, cached_at_unix_ms: u64, ) -> Result<Vec<Phase1MediaArtifactId>, TodayError> { + let _projection = self.today_projection_lock.lock().await; let storage = self .client .storage() .map_err(|_| TodayError::RuntimeUnavailable)?; let generation = projection_generation()?; - let mut state = load_state(storage, context, generation) - .await? - .ok_or(TodayError::ProjectionMissing)?; + let mut state = media_visibility::current_state(self, context).await?; let mut trial = state.clone(); let changed = mutate_matching_media(&mut trial, reference_fingerprint, |media| { media.verify(operation_id, receipt.clone()) @@ -652,6 +656,7 @@ impl TeraRuntime { observed_at_unix_ms: u64, ) -> Result<bool, TodayError> { let _command = self.lifecycle.enter()?; + let _projection = self.today_projection_lock.lock().await; let storage = self .client .storage() @@ -676,6 +681,7 @@ impl TeraRuntime { let _command = self.lifecycle.enter()?; #[cfg(feature = "mobile-social")] let _guard = self.inbound_media_lock.lock().await; + let _projection = self.today_projection_lock.lock().await; let storage = self .client .storage() @@ -706,6 +712,7 @@ impl TeraRuntime { let _command = self.lifecycle.enter()?; #[cfg(feature = "mobile-social")] let _guard = self.inbound_media_lock.lock().await; + let _projection = self.today_projection_lock.lock().await; let storage = self .client .storage() @@ -746,200 +753,6 @@ impl TeraRuntime { state.media_cache.status().map_err(TodayError::from) } - /// Resolves one renderable artifact only after rechecking the exact local - /// file. Missing or corrupt bytes atomically revoke all matching receipts. - #[cfg(feature = "mobile-social")] - pub async fn phase1_verified_media_artifact( - &self, - context: &LocalNetwork, - artifact_id: Phase1MediaArtifactId, - observed_at_unix_ms: u64, - ) -> Result<Option<Phase1LocalMediaArtifact>, TodayError> { - let _command = self.lifecycle.enter()?; - let _guard = self.inbound_media_lock.lock().await; - let directory = self - .inbound_media_directory - .as_deref() - .ok_or(Phase1InboundMediaError::CacheUnavailable)?; - let storage = self - .client - .storage() - .map_err(|_| TodayError::RuntimeUnavailable)?; - let generation = projection_generation()?; - let mut state = load_state(storage, context, generation) - .await? - .ok_or(TodayError::ProjectionMissing)?; - let Some(receipt) = verified_receipt(&state, artifact_id) else { - return Ok(None); - }; - match super::media::verified_artifact(directory, &receipt).await { - Ok(artifact) => { - if state.media_cache.touch(artifact_id, observed_at_unix_ms)? { - persist_media_state(storage, context, generation, &mut state).await?; - } - Ok(Some(artifact)) - } - Err(error) => { - state.media_cache.invalidate_artifact(artifact_id); - invalidate_artifact_references(&mut state, artifact_id); - persist_media_state(storage, context, generation, &mut state).await?; - let _ = super::media::remove_artifact_files(directory, artifact_id).await; - Err(error.into()) - } - } - } - - /// Completes one bounded BUD-01 retrieval, exact-byte verification, and - /// atomic content-addressed cache commit under the configured Blossom slot. - #[cfg(feature = "mobile-social")] - pub async fn phase1_retrieve_media( - &self, - context: &LocalNetwork, - reference_fingerprint: [u8; 32], - operation_id: [u8; 16], - policy: Phase1MediaCachePolicy, - cancellation: BlossomCancellation, - ) -> Result<Phase1LocalMediaArtifact, TodayError> { - let _command = self.lifecycle.enter()?; - let _guard = self.inbound_media_lock.lock().await; - let directory = self - .inbound_media_directory - .as_deref() - .ok_or(Phase1InboundMediaError::CacheUnavailable)?; - let blossom = self - .client - .blossom() - .map_err(|_| TodayError::RuntimeUnavailable)? - .cloned() - .ok_or(TodayError::RuntimeUnavailable)?; - let sdk_configuration = blossom - .config_fingerprint() - .ok_or(TodayError::RuntimeUnavailable)?; - let configuration = - Phase1MediaConfigurationFingerprint::new(*sdk_configuration.as_bytes())?; - let structural = load_structural_reference(self, context, reference_fingerprint).await?; - let started_at_unix_ms = inbound_now_unix_ms()?; - self.phase1_begin_media_retrieval( - context, - reference_fingerprint, - Phase1InboundMediaPending::new(operation_id, configuration, started_at_unix_ms)?, - ) - .await?; - let request = match inbound_request(&structural) { - Ok(request) => request, - Err(error) => { - record_inbound_failure( - self, - context, - reference_fingerprint, - operation_id, - "invalid_reference", - false, - ) - .await; - return Err(error); - } - }; - let sdk_receipt = match blossom.retrieve(request, cancellation).await { - Ok(receipt) => receipt, - Err(error) => { - record_inbound_failure( - self, - context, - reference_fingerprint, - operation_id, - error.code().trim_start_matches("blossom_"), - error.retryable(), - ) - .await; - return Err(error.into()); - } - }; - if sdk_receipt.config_fingerprint() != sdk_configuration { - record_inbound_failure( - self, - context, - reference_fingerprint, - operation_id, - "configuration_changed", - false, - ) - .await; - return Err(Phase1InboundMediaError::ConfigurationMismatch.into()); - } - let dimensions = sdk_receipt.dimensions(); - let receipt = match Phase1VerifiedMediaReceipt::from_commitment( - &structural, - sdk_receipt.final_url().clone(), - sdk_receipt.commitment(), - dimensions.width(), - dimensions.height(), - configuration, - sdk_receipt.verified_at_unix_ms(), - ) { - Ok(receipt) => receipt, - Err(error) => { - record_inbound_failure( - self, - context, - reference_fingerprint, - operation_id, - "verification_failed", - false, - ) - .await; - return Err(error.into()); - } - }; - let artifact = - match super::media::write_verified_artifact(directory, &receipt, sdk_receipt.bytes()) - .await - { - Ok(artifact) => artifact, - Err(error) => { - record_inbound_failure( - self, - context, - reference_fingerprint, - operation_id, - "cache_write_failed", - true, - ) - .await; - return Err(error.into()); - } - }; - let evicted = match self - .phase1_commit_media_receipt( - context, - reference_fingerprint, - operation_id, - receipt, - policy, - sdk_receipt.verified_at_unix_ms(), - ) - .await - { - Ok(evicted) => evicted, - Err(error) => { - record_inbound_failure( - self, - context, - reference_fingerprint, - operation_id, - "cache_commit_failed", - true, - ) - .await; - return Err(error); - } - }; - for artifact_id in evicted { - super::media::remove_artifact_files(directory, artifact_id).await?; - } - Ok(artifact) - } - /// Persists active-author delivery state as a local-only Today overlay. pub async fn phase1_set_local_author_overlay( &self, @@ -989,34 +802,6 @@ impl TeraRuntime { } #[cfg(feature = "mobile-social")] -async fn load_structural_reference( - runtime: &TeraRuntime, - context: &LocalNetwork, - reference_fingerprint: [u8; 32], -) -> Result<Phase1StructuralMediaReference, TodayError> { - let storage = runtime - .client - .storage() - .map_err(|_| TodayError::RuntimeUnavailable)?; - let state = load_state(storage, context, projection_generation()?) - .await? - .ok_or(TodayError::ProjectionMissing)?; - state - .cards - .iter() - .flat_map(|projected| projected.card.media.iter()) - .chain( - state - .profiles - .values() - .flat_map(|profile| [&profile.picture, &profile.banner].into_iter().flatten()), - ) - .find(|media| media.structural().fingerprint() == &reference_fingerprint) - .map(|media| media.structural().clone()) - .ok_or(TodayError::InvalidRequest) -} - -#[cfg(feature = "mobile-social")] fn inbound_request( structural: &Phase1StructuralMediaReference, ) -> Result<BlossomInboundRequest, TodayError> { diff --git a/core/crates/tera_core/src/runtime/product_surface/today_media_retrieval.rs b/core/crates/tera_core/src/runtime/product_surface/today_media_retrieval.rs @@ -0,0 +1,232 @@ +use super::*; + +impl TeraRuntime { + /// Resolves one renderable artifact only after rechecking the exact local + /// file. Missing or corrupt bytes atomically revoke all matching receipts. + #[cfg(feature = "mobile-social")] + pub async fn phase1_verified_media_artifact( + &self, + context: &LocalNetwork, + artifact_id: Phase1MediaArtifactId, + observed_at_unix_ms: u64, + ) -> Result<Option<Phase1LocalMediaArtifact>, TodayError> { + let _command = self.lifecycle.enter()?; + let _guard = self.inbound_media_lock.lock().await; + let directory = self + .inbound_media_directory + .as_deref() + .ok_or(Phase1InboundMediaError::CacheUnavailable)?; + let storage = self + .client + .storage() + .map_err(|_| TodayError::RuntimeUnavailable)?; + let _projection = self.today_projection_lock.lock().await; + let generation = projection_generation()?; + let mut state = media_visibility::current_state(self, context).await?; + let Some(receipt) = verified_receipt(&state, artifact_id) else { + return Ok(None); + }; + match super::super::media::verified_artifact(directory, &receipt).await { + Ok(artifact) => { + state = media_visibility::current_state(self, context).await?; + if state.media_cache.touch(artifact_id, observed_at_unix_ms)? { + persist_media_state(storage, context, generation, &mut state).await?; + } + Ok(Some(artifact)) + } + Err(error) => { + state = media_visibility::current_state(self, context).await?; + state.media_cache.invalidate_artifact(artifact_id); + invalidate_artifact_references(&mut state, artifact_id); + persist_media_state(storage, context, generation, &mut state).await?; + let _ = super::super::media::remove_artifact_files(directory, artifact_id).await; + Err(error.into()) + } + } + } + + /// Completes one bounded BUD-01 retrieval, exact-byte verification, and + /// atomic content-addressed cache commit under the configured Blossom slot. + #[cfg(feature = "mobile-social")] + pub async fn phase1_retrieve_media( + &self, + context: &LocalNetwork, + reference_fingerprint: [u8; 32], + operation_id: [u8; 16], + policy: Phase1MediaCachePolicy, + cancellation: BlossomCancellation, + ) -> Result<Phase1LocalMediaArtifact, TodayError> { + let _command = self.lifecycle.enter()?; + let directory = self + .inbound_media_directory + .as_deref() + .ok_or(Phase1InboundMediaError::CacheUnavailable)?; + let blossom = self + .client + .blossom() + .map_err(|_| TodayError::RuntimeUnavailable)? + .cloned() + .ok_or(TodayError::RuntimeUnavailable)?; + let sdk_configuration = blossom + .config_fingerprint() + .ok_or(TodayError::RuntimeUnavailable)?; + let configuration = + Phase1MediaConfigurationFingerprint::new(*sdk_configuration.as_bytes())?; + let structural = load_structural_reference(self, context, reference_fingerprint).await?; + let started_at_unix_ms = inbound_now_unix_ms()?; + if !self + .phase1_begin_media_retrieval( + context, + reference_fingerprint, + Phase1InboundMediaPending::new(operation_id, configuration, started_at_unix_ms)?, + ) + .await? + { + return Err(TodayError::InvalidRequest); + } + let request = match inbound_request(&structural) { + Ok(request) => request, + Err(error) => { + record_inbound_failure( + self, + context, + reference_fingerprint, + operation_id, + "invalid_reference", + false, + ) + .await; + return Err(error); + } + }; + let sdk_receipt = match blossom.retrieve(request, cancellation).await { + Ok(receipt) => receipt, + Err(error) => { + record_inbound_failure( + self, + context, + reference_fingerprint, + operation_id, + error.code().trim_start_matches("blossom_"), + error.retryable(), + ) + .await; + return Err(error.into()); + } + }; + if sdk_receipt.config_fingerprint() != sdk_configuration { + record_inbound_failure( + self, + context, + reference_fingerprint, + operation_id, + "configuration_changed", + false, + ) + .await; + return Err(Phase1InboundMediaError::ConfigurationMismatch.into()); + } + let dimensions = sdk_receipt.dimensions(); + let receipt = match Phase1VerifiedMediaReceipt::from_commitment( + &structural, + sdk_receipt.final_url().clone(), + sdk_receipt.commitment(), + dimensions.width(), + dimensions.height(), + configuration, + sdk_receipt.verified_at_unix_ms(), + ) { + Ok(receipt) => receipt, + Err(error) => { + record_inbound_failure( + self, + context, + reference_fingerprint, + operation_id, + "verification_failed", + false, + ) + .await; + return Err(error.into()); + } + }; + // Serialize only the local file/receipt phase, never the network wait. + let _guard = self.inbound_media_lock.lock().await; + // A completed network request does not retain obsolete visibility. + if load_structural_reference(self, context, reference_fingerprint).await? != structural { + return Err(TodayError::InvalidRequest); + } + let artifact = match super::super::media::write_verified_artifact( + directory, + &receipt, + sdk_receipt.bytes(), + ) + .await + { + Ok(artifact) => artifact, + Err(error) => { + record_inbound_failure( + self, + context, + reference_fingerprint, + operation_id, + "cache_write_failed", + true, + ) + .await; + return Err(error.into()); + } + }; + let evicted = match self + .phase1_commit_media_receipt( + context, + reference_fingerprint, + operation_id, + receipt, + policy, + sdk_receipt.verified_at_unix_ms(), + ) + .await + { + Ok(evicted) => evicted, + Err(error) => { + record_inbound_failure( + self, + context, + reference_fingerprint, + operation_id, + "cache_commit_failed", + true, + ) + .await; + return Err(error); + } + }; + for artifact_id in evicted { + super::super::media::remove_artifact_files(directory, artifact_id).await?; + } + Ok(artifact) + } +} + +#[cfg(feature = "mobile-social")] +async fn load_structural_reference( + runtime: &TeraRuntime, + context: &LocalNetwork, + reference_fingerprint: [u8; 32], +) -> Result<Phase1StructuralMediaReference, TodayError> { + let state = media_visibility::current_state(runtime, context).await?; + state + .cards + .iter() + .flat_map(|projected| projected.card.media.iter()) + .chain( + state + .profiles + .values() + .flat_map(|profile| [&profile.picture, &profile.banner].into_iter().flatten()), + ) + .find(|media| media.structural().fingerprint() == &reference_fingerprint) + .map(|media| media.structural().clone()) + .ok_or(TodayError::InvalidRequest) +} diff --git a/core/crates/tera_core/src/runtime/product_surface/today_media_visibility.rs b/core/crates/tera_core/src/runtime/product_surface/today_media_visibility.rs @@ -0,0 +1,30 @@ +use super::*; + +#[cfg(all(test, feature = "mobile-social"))] +#[path = "today_media_visibility_tests.rs"] +mod tests; + +/// A cached reference is not authority after the selected query or visible +/// event set changes. The owner re-evaluates replacement/deletion semantics; +/// raw row counts and the store's identity generation cannot establish this. +pub(super) async fn current_state( + runtime: &TeraRuntime, + context: &LocalNetwork, +) -> Result<TodayProjectionState, TodayError> { + let scope = paging_scope::query_scope(context, runtime.store_public_key)?; + let storage = runtime + .client + .storage() + .map_err(|_| TodayError::RuntimeUnavailable)?; + let state = load_state(storage, context, projection_generation()?) + .await? + .ok_or(TodayError::ProjectionMissing)?; + if state.query_scope != Some(scope) { + return Err(TodayError::InvalidRequest); + } + let visibility = EventStore::rebuild_visibility(storage).await?; + if state.visibility_digest != Some(*visibility.digest().as_bytes()) { + return Err(TodayError::InvalidRequest); + } + Ok(state) +} diff --git a/core/crates/tera_core/src/runtime/product_surface/today_media_visibility_tests.rs b/core/crates/tera_core/src/runtime/product_surface/today_media_visibility_tests.rs @@ -0,0 +1,337 @@ +use super::super::tests::{context, ingest, keys, signed, visible_admission}; +use super::*; +use crate::runtime::{ + builder::RuntimeBuilder, + store::{MobileUserStoreConfig, ProtectedDataAvailability}, +}; +use radroots_blossom::Sha256 as BlobHash; +use radroots_sdk::transport::{ + BlossomConfig, BlossomEndpointAuthority, BlossomHostKind, BlossomProfile, +}; +use radroots_transport::source::ObservedEvent; +use std::{sync::Arc, time::Duration}; +use tokio::{ + io::{AsyncReadExt, AsyncWriteExt}, + net::TcpListener, + sync::oneshot, +}; + +const NOW: u64 = 2_000_000_000; + +struct Fixture { + _root: tempfile::TempDir, + runtime: Arc<TeraRuntime>, + selected: LocalNetwork, + fingerprint: [u8; 32], + source: String, + bytes: Vec<u8>, + listener: TcpListener, +} + +async fn fixture() -> Fixture { + let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); + let origin = format!("http://{}", listener.local_addr().unwrap()); + let mut bytes = b"\x89PNG\r\n\x1a\n\0\0\0\rIHDR".to_vec(); + bytes.extend_from_slice(&2_u32.to_be_bytes()); + bytes.extend_from_slice(&3_u32.to_be_bytes()); + let url = format!("{origin}/{}.png", BlobHash::digest(&bytes).to_hex()); + let root = tempfile::tempdir().unwrap(); + let public_key = keys().public_key().to_string(); + let store = MobileUserStoreConfig::from_encoded( + root.path(), + &public_key, + &"86".repeat(32), + NOW * 1_000, + ProtectedDataAvailability::Available, + ) + .unwrap(); + std::fs::create_dir_all(store.owner_directory()).unwrap(); + let blossom = BlossomConfig::from_profile( + BlossomProfile::new( + BlossomHostKind::Simulator, + BlossomEndpointAuthority::LoopbackDevelopment, + &origin, + std::iter::empty::<&str>(), + ) + .unwrap(), + ) + .with_network_policy( + Duration::from_millis(250), + Duration::from_secs(3), + 1, + Duration::from_millis(1), + ) + .unwrap(); + let runtime = Arc::new( + RuntimeBuilder::new(store) + .blossom_config(blossom) + .build() + .await + .unwrap(), + ); + let selected = context(None, 1); + let profile = signed( + 0, + vec![], + &format!(r#"{{"name":"Media fixture","picture":"{url}"}}"#), + NOW, + ); + let source = profile.id().to_hex(); + ingest(&runtime, &selected, profile, NOW + 1).await; + let picture = runtime + .phase1_me(&selected, &public_key, NOW + 2, "UTC") + .await + .unwrap() + .profile + .unwrap() + .picture + .unwrap(); + Fixture { + _root: root, + runtime, + selected, + fingerprint: *picture.structural().fingerprint(), + source, + bytes, + listener, + } +} + +async fn retrieve( + runtime: &TeraRuntime, + selected: &LocalNetwork, + fingerprint: [u8; 32], +) -> Result<Phase1LocalMediaArtifact, TodayError> { + runtime + .phase1_retrieve_media( + selected, + fingerprint, + [86; 16], + Phase1MediaCachePolicy::new(1_024, 8).unwrap(), + BlossomCancellation::default(), + ) + .await +} + +async fn serve( + listener: TcpListener, + bytes: Vec<u8>, + started: oneshot::Sender<()>, + resume: oneshot::Receiver<()>, +) { + let (mut stream, _) = listener.accept().await.unwrap(); + let mut request = Vec::new(); + while !request.windows(4).any(|part| part == b"\r\n\r\n") { + let mut chunk = [0; 1_024]; + let count = stream.read(&mut chunk).await.unwrap(); + assert_ne!(count, 0); + request.extend_from_slice(&chunk[..count]); + assert!(request.len() <= 16_384); + } + let request = String::from_utf8(request).unwrap(); + assert!(request.starts_with("GET /")); + assert!(!request.to_lowercase().contains("authorization:")); + started.send(()).unwrap(); + resume.await.unwrap(); + let header = format!( + "HTTP/1.1 200 OK\r\nContent-Type: image/png\r\nContent-Length: {}\r\nConnection: close\r\n\r\n", + bytes.len() + ); + stream.write_all(header.as_bytes()).await.unwrap(); + stream.write_all(&bytes).await.unwrap(); + stream.shutdown().await.unwrap(); +} + +#[tokio::test] +async fn wrong_query_scope_and_unadmitted_reference_cannot_start_transport() { + let f = fixture().await; + let wrong = context(Some("another locality"), 1); + for (selected, fingerprint) in [(&wrong, f.fingerprint), (&f.selected, [1; 32])] { + assert!(matches!( + retrieve(&f.runtime, selected, fingerprint).await, + Err(TodayError::InvalidRequest) + )); + } + assert!( + tokio::time::timeout(Duration::from_millis(30), f.listener.accept()) + .await + .is_err() + ); + let (started, entered) = oneshot::channel(); + let (resume, wait) = oneshot::channel(); + let server = tokio::spawn(serve(f.listener, f.bytes.clone(), started, wait)); + resume.send(()).unwrap(); + let artifact = retrieve(&f.runtime, &f.selected, f.fingerprint) + .await + .unwrap(); + entered.await.unwrap(); + server.await.unwrap(); + assert_eq!(artifact.bytes(), f.bytes); + assert!(matches!( + f.runtime + .phase1_verified_media_artifact(&wrong, artifact.artifact_id(), (NOW + 3) * 1_000) + .await, + Err(TodayError::InvalidRequest) + )); + let deletion = signed(5, vec![vec!["e", &f.source]], "", NOW + 4); + EventStore::admit( + f.runtime.client.storage().unwrap(), + visible_admission(deletion, (NOW + 4) * 1_000), + ) + .await + .unwrap(); + assert!(matches!( + f.runtime + .phase1_verified_media_artifact(&f.selected, artifact.artifact_id(), (NOW + 5) * 1_000) + .await, + Err(TodayError::InvalidRequest) + )); + assert!( + artifact.local_path().exists(), + "visibility revocation does not authorize shared blob deletion" + ); + f.runtime.shutdown().await.unwrap(); +} + +#[tokio::test] +async fn same_raw_count_visibility_change_revokes_cached_retrieval_authority() { + let f = fixture().await; + let deletion = signed(5, vec![vec!["e", &f.source]], "", NOW + 4); + let admission = visible_admission(deletion, (NOW + 4) * 1_000); + let storage = f.runtime.client.storage().unwrap(); + EventStore::admit( + storage, + EventAdmission::raw(ObservedEvent::new( + admission.event().clone(), + admission.provenance().clone(), + )), + ) + .await + .unwrap(); + f.runtime + .phase1_refresh_today(&f.selected, NOW + 5, TodayProjectionUpdate::Incremental) + .await + .unwrap(); + let before = EventStore::status(storage).await.unwrap().raw_events(); + EventStore::admit(storage, admission).await.unwrap(); + assert_eq!( + EventStore::status(storage).await.unwrap().raw_events(), + before + ); + assert!(matches!( + retrieve(&f.runtime, &f.selected, f.fingerprint).await, + Err(TodayError::InvalidRequest) + )); + assert!( + tokio::time::timeout(Duration::from_millis(30), f.listener.accept()) + .await + .is_err() + ); + f.runtime.shutdown().await.unwrap(); +} + +#[tokio::test] +async fn retraction_during_network_wait_cannot_publish_verified_bytes() { + let f = fixture().await; + let (started, entered) = oneshot::channel(); + let (resume, wait) = oneshot::channel(); + let server = tokio::spawn(serve(f.listener, f.bytes, started, wait)); + let runtime = f.runtime.clone(); + let selected = f.selected.clone(); + let fingerprint = f.fingerprint; + let pending = tokio::spawn(async move { retrieve(&runtime, &selected, fingerprint).await }); + tokio::time::timeout(Duration::from_secs(2), entered) + .await + .unwrap() + .unwrap(); + let deletion = signed(5, vec![vec!["e", &f.source]], "", NOW + 4); + ingest(&f.runtime, &f.selected, deletion, NOW + 5).await; + resume.send(()).unwrap(); + assert!(matches!( + pending.await.unwrap(), + Err(TodayError::InvalidRequest) + )); + server.await.unwrap(); + assert_eq!( + f.runtime + .phase1_media_cache_status(&f.selected) + .await + .unwrap() + .artifacts, + 0 + ); + f.runtime.shutdown().await.unwrap(); +} + +#[tokio::test] +async fn arbitrary_embedded_url_cannot_bypass_the_verified_transport() { + let f = fixture().await; + let url = format!("http://{}/arbitrary", f.listener.local_addr().unwrap()); + ingest( + &f.runtime, + &f.selected, + signed(0, vec![], &format!(r#"{{"picture":"{url}"}}"#), NOW + 10), + NOW + 11, + ) + .await; + let picture = f + .runtime + .phase1_me( + &f.selected, + &keys().public_key().to_string(), + NOW + 12, + "UTC", + ) + .await + .unwrap() + .profile + .unwrap() + .picture + .unwrap(); + assert!( + retrieve(&f.runtime, &f.selected, *picture.structural().fingerprint()) + .await + .is_err() + ); + assert!( + tokio::time::timeout(Duration::from_millis(30), f.listener.accept()) + .await + .is_err() + ); + assert_eq!( + f.runtime + .phase1_media_cache_status(&f.selected) + .await + .unwrap() + .artifacts, + 0 + ); + f.runtime.shutdown().await.unwrap(); +} + +#[tokio::test] +async fn visible_reference_does_not_turn_wrong_response_bytes_into_an_artifact() { + let f = fixture().await; + let mut wrong = f.bytes; + wrong[23] ^= 1; + let (started, entered) = oneshot::channel(); + let (resume, wait) = oneshot::channel(); + let server = tokio::spawn(serve(f.listener, wrong, started, wait)); + resume.send(()).unwrap(); + assert!( + retrieve(&f.runtime, &f.selected, f.fingerprint) + .await + .is_err() + ); + entered.await.unwrap(); + server.await.unwrap(); + assert_eq!( + f.runtime + .phase1_media_cache_status(&f.selected) + .await + .unwrap() + .artifacts, + 0 + ); + f.runtime.shutdown().await.unwrap(); +} diff --git a/release/provenance.json b/release/provenance.json @@ -2,7 +2,7 @@ "artifacts": { "app_api_sha256": "020924097c0d7efc33128cb8fd3d3b2026d95f57c44da71880e585aff80f070b", "ffi_api_sha256": "7b3f92a57ebcc7873349403243098d5dcf993b2dbdab9ed84fea4a50c20baab0", - "ffi_provenance_sha256": "6e9f9645c78446371d5fdfc90007ca89d09ab102cc17f417d03d7eaff08a9810", + "ffi_provenance_sha256": "386966b5d6d0f9a1639d26520636d725fe1f035d74f776132c9fb4190e8d6eb7", "info_plist_sha256": "15ef08b1cdd1096cfb9eeaf5be5bf8f814807a7ca9350bbbb47860fa72ec13ef", "privacy_manifest_sha256": "a331d51864743ebe4e00dd22360b4a538b6b3ac26a6b3eb54094e60a36959a12", "sbom_sha256": "b202075f3c34a3a70e8745ca6d6acab6c2cf5e2d6de9a2b2647475d73a8d3f28", @@ -22,7 +22,7 @@ "lib_revision": "963dd8dc99fc5f16ef7d7eea11f8d964770a696e", "source_date_epoch": 1787871027, "swift_package_lock_sha256": "d5c9240384b50f006ee6e9eff886a3775546280e90a05cbe781eaa296632da26", - "tera_ffi_source_tree": "f1bd4bd0656de8a67a9602889c0655fb3bdadc56", + "tera_ffi_source_tree": "e8bc8e378ae5db8ad84891b9262670a1aaf4e395", "xcode_package_lock_sha256": "b6806f710734f8e628d7a7d7b97826d89eea78fba32fa9b464f513bcadd411e2" }, "version": "0.1.0-alpha" diff --git a/test-fixtures/legacy-identifiers.v1.json b/test-fixtures/legacy-identifiers.v1.json @@ -4261,6 +4261,10 @@ "count": 2 }, { + "path": "core/crates/tera_core/src/runtime/product_surface/today_media_visibility_tests.rs", + "count": 1 + }, + { "path": "core/crates/tera_ffi/Cargo.toml", "count": 1 }, @@ -4895,6 +4899,10 @@ "count": 1 }, { + "path": "core/crates/tera_core/src/runtime/product_surface/today_media_visibility_tests.rs", + "count": 1 + }, + { "path": "core/crates/tera_core/src/runtime/product_surface/today_performance_tests.rs", "count": 1 }, @@ -5323,6 +5331,10 @@ "count": 2 }, { + "path": "core/crates/tera_core/src/runtime/product_surface/today_media_visibility_tests.rs", + "count": 1 + }, + { "path": "core/crates/tera_core/src/runtime/product_surface/today_sync.rs", "count": 1 },