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 bfc94b3dc433e349be86b3c0e3d1cb01573b9f81
parent da90092211a6f08618c05dbd2138300f1cd0bb8b
Author: triesap <tyson@radroots.org>
Date:   Mon, 10 Aug 2026 21:59:13 +0000

mobile: expose verified media bytes to native hosts

Diffstat:
Mcore/crates/tera_core/src/runtime/product_surface/media.rs | 24++++++++++++++++--------
Mcore/crates/tera_core/src/runtime/product_surface/settings.rs | 3++-
Mcore/crates/tera_core/src/runtime/product_surface/today.rs | 1+
3 files changed, 19 insertions(+), 9 deletions(-)

diff --git a/core/crates/tera_core/src/runtime/product_surface/media.rs b/core/crates/tera_core/src/runtime/product_surface/media.rs @@ -549,6 +549,7 @@ impl Phase1VerifiedMediaReceipt { pub struct Phase1LocalMediaArtifact { artifact_id: Phase1MediaArtifactId, local_path: PathBuf, + bytes: Vec<u8>, byte_size: u64, media_type: String, width: u32, @@ -580,6 +581,10 @@ impl Phase1LocalMediaArtifact { self.local_path.as_path() } + pub fn bytes(&self) -> &[u8] { + self.bytes.as_slice() + } + pub const fn byte_size(&self) -> u64 { self.byte_size } @@ -1067,8 +1072,8 @@ pub(crate) async fn write_verified_artifact( let final_path = artifact_path(directory, receipt.artifact_id, receipt.extension.as_str())?; match tokio::fs::symlink_metadata(&final_path).await { Ok(_) => { - verify_artifact_file(&final_path, receipt).await?; - return Ok(local_artifact(final_path, receipt)); + let verified_bytes = verify_artifact_file(&final_path, receipt).await?; + return Ok(local_artifact(final_path, receipt, verified_bytes)); } Err(error) if error.kind() == std::io::ErrorKind::NotFound => {} Err(_) => return Err(Phase1InboundMediaError::CacheIo), @@ -1116,8 +1121,8 @@ pub(crate) async fn write_verified_artifact( if write_result.is_err() { let _ = tokio::fs::remove_file(&temporary_path).await; } - write_result?; - Ok(local_artifact(final_path, receipt)) + let verified_bytes = write_result?; + Ok(local_artifact(final_path, receipt, verified_bytes)) } #[cfg(feature = "mobile-social")] @@ -1150,8 +1155,8 @@ pub(crate) async fn verified_artifact( receipt.validate_intrinsic()?; ensure_cache_directory(directory).await?; let path = artifact_path(directory, receipt.artifact_id, receipt.extension.as_str())?; - verify_artifact_file(&path, receipt).await?; - Ok(local_artifact(path, receipt)) + let verified_bytes = verify_artifact_file(&path, receipt).await?; + Ok(local_artifact(path, receipt, verified_bytes)) } #[cfg(feature = "mobile-social")] @@ -1186,7 +1191,7 @@ fn artifact_path( async fn verify_artifact_file( path: &Path, receipt: &Phase1VerifiedMediaReceipt, -) -> Result<(), Phase1InboundMediaError> { +) -> Result<Vec<u8>, Phase1InboundMediaError> { let metadata = tokio::fs::symlink_metadata(path) .await .map_err(|_| Phase1InboundMediaError::CorruptArtifact)?; @@ -1202,7 +1207,7 @@ async fn verify_artifact_file( if Sha256::digest(bytes.as_slice()).to_hex() != receipt.observed_sha256 { return Err(Phase1InboundMediaError::CorruptArtifact); } - Ok(()) + Ok(bytes) } #[cfg(feature = "mobile-social")] @@ -1218,10 +1223,12 @@ async fn sync_cache_directory(directory: &Path) -> Result<(), Phase1InboundMedia fn local_artifact( local_path: PathBuf, receipt: &Phase1VerifiedMediaReceipt, + bytes: Vec<u8>, ) -> Phase1LocalMediaArtifact { Phase1LocalMediaArtifact { artifact_id: receipt.artifact_id, local_path, + bytes, byte_size: receipt.byte_size, media_type: receipt.media_type.clone(), width: receipt.width, @@ -1549,6 +1556,7 @@ mod tests { let left = left.unwrap(); let right = right.unwrap(); assert_eq!(left, right); + assert_eq!(left.bytes(), bytes); assert_eq!(tokio::fs::read(left.local_path()).await.unwrap(), bytes); assert_eq!( std::fs::read_dir(&directory).unwrap().count(), diff --git a/core/crates/tera_core/src/runtime/product_surface/settings.rs b/core/crates/tera_core/src/runtime/product_surface/settings.rs @@ -1520,7 +1520,8 @@ mod tests { #[test] fn settings_reject_mixed_network_environments() { - let settings = MobileSettings::default().with_blossom(BlossomPreferences::simulator_default()); + let settings = + MobileSettings::default().with_blossom(BlossomPreferences::simulator_default()); assert_eq!( ReplaceMobileSettings::new(settings.revision(), settings).unwrap_err(), SettingsError::NetworkEnvironmentMismatch diff --git a/core/crates/tera_core/src/runtime/product_surface/today.rs b/core/crates/tera_core/src/runtime/product_surface/today.rs @@ -3209,6 +3209,7 @@ mod tests { .starts_with(owner_directory.join("inbound_media.v1")) ); assert_eq!(tokio::fs::read(artifact.local_path()).await.unwrap(), bytes); + assert_eq!(artifact.bytes(), bytes); assert!( !tokio::fs::symlink_metadata(artifact.local_path()) .await