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 718cb65037a8b5336eb3063886c4a1cc69a9f14e
parent 2292464ef61c4b953f496da98c0962c286db43e2
Author: triesap <tyson@radroots.org>
Date:   Mon, 10 Aug 2026 17:32:12 +0000

feat: persist verified inbound media receipts

Separate signed structural references from retrieval state and exact-byte receipts.

Persist a configuration-scoped, content-addressed, bounded LRU cache and fail closed across eviction, corruption, metadata drift, and legacy migration.

Diffstat:
Mcore/crates/tera_ffi/src/dto.rs | 35++++++++++++++++++-----------------
Mcore/crates/tera_ffi/src/error.rs | 1+
2 files changed, 19 insertions(+), 17 deletions(-)

diff --git a/core/crates/tera_ffi/src/dto.rs b/core/crates/tera_ffi/src/dto.rs @@ -22,9 +22,9 @@ use radroots_mobile_core::runtime::{ product_surface::{ AddCommandType, CardLifecycleState, CreateAsk, CreateEvent, CreateFoodAvailability, CreatePhotoUpdate, CreateUpdate, LocalNetwork, LocalNetworkRelayPolicy, MeSnapshot, - MediaReference, MediaVerificationState, Phase1AddCommand, Phase1CancellationPolicy, - Phase1DraftEventTiming, Phase1DraftFormSnapshot, Phase1DraftKind, Phase1DraftMediaSnapshot, - Phase1DraftStatus, Phase1MediaPrerequisite, Phase1MediaStage, Phase1OutboxState, + MediaReference, Phase1AddCommand, Phase1CancellationPolicy, Phase1DraftEventTiming, + Phase1DraftFormSnapshot, Phase1DraftKind, Phase1DraftMediaSnapshot, Phase1DraftStatus, + Phase1InboundMediaState, Phase1MediaPrerequisite, Phase1MediaStage, Phase1OutboxState, Phase1QueuePolicy, Phase1RelaySatisfaction, Phase1UploadIntent, ProfileSummary, SearchResult, SearchResultType, SupportingProfile, ThreadEntry, TodayCard, TodayCardType, TodayPage, TodayProjectionUpdate, TodayRefreshReceipt, TodayRelaySyncState, @@ -262,13 +262,13 @@ pub enum FfiMediaVerificationState { } #[cfg_attr(coverage_nightly, coverage(off))] -impl From<MediaVerificationState> for FfiMediaVerificationState { - fn from(value: MediaVerificationState) -> Self { +impl From<&Phase1InboundMediaState> for FfiMediaVerificationState { + fn from(value: &Phase1InboundMediaState) -> Self { match value { - MediaVerificationState::Pending => Self::Pending, - MediaVerificationState::Verified => Self::Verified, - MediaVerificationState::Failed => Self::Failed, - MediaVerificationState::Unavailable => Self::Unavailable, + Phase1InboundMediaState::Pending(_) => Self::Pending, + Phase1InboundMediaState::Verified(_) => Self::Verified, + Phase1InboundMediaState::Failed(_) => Self::Failed, + Phase1InboundMediaState::Unavailable => Self::Unavailable, } } } @@ -289,16 +289,17 @@ pub struct FfiMediaReferenceRecord { #[cfg_attr(coverage_nightly, coverage(off))] impl From<MediaReference> for FfiMediaReferenceRecord { fn from(value: MediaReference) -> Self { + let structural = value.structural(); Self { schema_version: MOBILE_FFI_SCHEMA_VERSION, - url: value.url, - sha256: value.sha256, - media_type: value.media_type, - width: value.width, - height: value.height, - byte_size: value.byte_size, - alt: value.alt, - verification: value.verification.into(), + url: structural.source_url().to_owned(), + sha256: structural.expected_sha256().map(str::to_owned), + media_type: structural.expected_media_type().map(str::to_owned), + width: structural.expected_width(), + height: structural.expected_height(), + byte_size: structural.expected_byte_size(), + alt: structural.alt().map(str::to_owned), + verification: value.retrieval().into(), } } } diff --git a/core/crates/tera_ffi/src/error.rs b/core/crates/tera_ffi/src/error.rs @@ -147,6 +147,7 @@ impl From<TodayError> for RadrootsAppError { ("today_cursor_invalid", true, &["restart_pagination"][..]) } TodayError::RuntimeUnavailable => ("today_runtime_unavailable", true, &["retry"][..]), + TodayError::InboundMedia(_) => ("today_media_invalid", false, &["retry_media"][..]), TodayError::CorruptProjection | TodayError::Serialization | TodayError::Storage(_) => { ("today_state_failed", true, &["rebuild", "retry"][..]) }