commit d4751fff1abb47746701012928957ee8c9af4cf0
parent 54642535cfc06b34f7e56ca73a6779b56ee3d65d
Author: triesap <tyson@radroots.org>
Date: Fri, 7 Aug 2026 15:30:36 +0000
feat(mobile): enforce opaque host signing
- replace local secret slots and duplicate social authoring with focused host signer operations
- domain-separate BUD-11 HTTP authorization from durable relay event plans
- revalidate signer output against exact request deadline cancellation and signature before commit
- refresh public APIs docs and tests across consolidated mobile surfaces
Diffstat:
3 files changed, 0 insertions(+), 315 deletions(-)
diff --git a/core/crates/tera_ffi/src/remote.rs b/core/crates/tera_ffi/src/remote.rs
@@ -2,8 +2,6 @@
use radroots_mobile_core::runtime::app_info::*;
use radroots_mobile_core::runtime::info::*;
-use radroots_mobile_core::runtime::key_management::*;
-use radroots_mobile_core::runtime::nostr::*;
use radroots_mobile_core::runtime::product_surface::*;
use radroots_mobile_core::runtime::sdk::*;
use radroots_mobile_core::{SdkErrorRecord, StoreErrorRecord};
@@ -89,81 +87,6 @@ pub struct SdkShutdownRecord {
pub already_closed: bool,
}
-#[uniffi::remote(Record)]
-pub struct NostrIdentityRecord {
- pub id: String,
- pub public_key_hex: String,
- pub public_key_npub: String,
- pub label: Option<String>,
- pub is_selected: bool,
-}
-
-#[uniffi::remote(Record)]
-pub struct NostrIdentitySnapshot {
- pub has_selected_signing_identity: bool,
- pub selected_identity_id: Option<String>,
- pub selected_npub: Option<String>,
- pub identities: Vec<NostrIdentityRecord>,
-}
-
-#[uniffi::remote(Record)]
-pub struct NostrHostCustodyIdentity {
- pub id: String,
- pub public_key_hex: String,
- pub public_key_npub: String,
-}
-
-#[uniffi::remote(Enum)]
-pub enum NostrLight {
- Red,
- Yellow,
- Green,
-}
-
-#[uniffi::remote(Record)]
-pub struct NostrConnectionStatus {
- pub light: NostrLight,
- pub configured: bool,
- pub source_available: bool,
- pub sink_available: bool,
- pub last_error: Option<String>,
-}
-
-#[uniffi::remote(Record)]
-pub struct NostrProfile {
- pub name: Option<String>,
- pub display_name: Option<String>,
- pub nip05: Option<String>,
- pub about: Option<String>,
- pub website: Option<String>,
- pub picture: Option<String>,
- pub banner: Option<String>,
- pub lud06: Option<String>,
- pub lud16: Option<String>,
- pub bot: Option<String>,
-}
-
-#[uniffi::remote(Record)]
-pub struct NostrProfileEventMetadata {
- pub id: String,
- pub author: String,
- pub published_at: u64,
- pub profile: NostrProfile,
-}
-
-#[uniffi::remote(Record)]
-pub struct NostrPost {
- pub content: String,
-}
-
-#[uniffi::remote(Record)]
-pub struct NostrPostEventMetadata {
- pub id: String,
- pub author: String,
- pub published_at: u64,
- pub post: NostrPost,
-}
-
#[uniffi::remote(Enum)]
pub enum TodayCardType {
Update,
diff --git a/core/crates/tera_ffi/src/runtime.rs b/core/crates/tera_ffi/src/runtime.rs
@@ -1,7 +1,5 @@
use radroots_mobile_core::runtime::{
info::RuntimeInfo,
- key_management::{NostrHostCustodyIdentity, NostrIdentityRecord, NostrIdentitySnapshot},
- nostr::{NostrConnectionStatus, NostrPostEventMetadata, NostrProfileEventMetadata},
product_surface::{AddCommandType, CardAddParity, LocalNetwork, TodayCardType},
sdk::{SdkCapabilityRecord, SdkShutdownRecord, SdkStorageStatusRecord},
};
@@ -91,147 +89,6 @@ impl RadrootsRuntime {
self.inner.sdk_storage_status().await.map_err(Into::into)
}
- pub fn nostr_identity_has_selected_signing_identity(&self) -> bool {
- self.inner.nostr_identity_has_selected_signing_identity()
- }
-
- pub fn nostr_identity_selected_npub(&self) -> Option<String> {
- self.inner.nostr_identity_selected_npub()
- }
-
- pub fn nostr_identity_list(&self) -> Result<Vec<NostrIdentityRecord>, RadrootsAppError> {
- self.inner.nostr_identity_list().map_err(Into::into)
- }
-
- pub fn nostr_identity_list_ids(&self) -> Result<Vec<String>, RadrootsAppError> {
- self.inner.nostr_identity_list_ids().map_err(Into::into)
- }
-
- pub fn nostr_identity_snapshot(&self) -> Result<NostrIdentitySnapshot, RadrootsAppError> {
- self.inner.nostr_identity_snapshot().map_err(Into::into)
- }
-
- pub fn nostr_identity_validate_host_custody_secret(
- &self,
- secret_key: String,
- ) -> Result<NostrHostCustodyIdentity, RadrootsAppError> {
- self.inner
- .nostr_identity_validate_host_custody_secret(secret_key)
- .map_err(Into::into)
- }
-
- pub fn nostr_identity_restore_host_custody_secret(
- &self,
- secret_key: String,
- label: Option<String>,
- make_selected: bool,
- ) -> Result<NostrIdentityRecord, RadrootsAppError> {
- self.inner
- .nostr_identity_restore_host_custody_secret(secret_key, label, make_selected)
- .map_err(Into::into)
- }
-
- pub fn nostr_identity_select(&self, identity_id: String) -> Result<(), RadrootsAppError> {
- self.inner
- .nostr_identity_select(identity_id)
- .map_err(Into::into)
- }
-
- pub fn nostr_identity_remove(&self, identity_id: String) -> Result<(), RadrootsAppError> {
- self.inner
- .nostr_identity_remove(identity_id)
- .map_err(Into::into)
- }
-
- pub fn nostr_identity_lock_host_custody_runtime(&self) -> Result<(), RadrootsAppError> {
- self.inner
- .nostr_identity_lock_host_custody_runtime()
- .map_err(Into::into)
- }
-
- pub fn nostr_identity_reset_host_custody_runtime(&self) -> Result<(), RadrootsAppError> {
- self.inner
- .nostr_identity_reset_host_custody_runtime()
- .map_err(Into::into)
- }
-
- pub fn nostr_set_default_relays(&self, relays: Vec<String>) -> Result<(), RadrootsAppError> {
- self.inner
- .nostr_set_default_relays(relays)
- .map_err(Into::into)
- }
-
- pub fn nostr_connect_if_key_present(&self) -> Result<(), RadrootsAppError> {
- self.inner
- .nostr_connect_if_key_present()
- .map_err(Into::into)
- }
-
- pub async fn nostr_connection_status(&self) -> Result<NostrConnectionStatus, RadrootsAppError> {
- self.inner
- .nostr_connection_status()
- .await
- .map_err(Into::into)
- }
-
- pub async fn nostr_profile_for_self(
- &self,
- ) -> Result<Option<NostrProfileEventMetadata>, RadrootsAppError> {
- self.inner
- .nostr_profile_for_self()
- .await
- .map_err(Into::into)
- }
-
- pub async fn nostr_post_profile(
- &self,
- name: Option<String>,
- display_name: Option<String>,
- nip05: Option<String>,
- about: Option<String>,
- ) -> Result<String, RadrootsAppError> {
- self.inner
- .nostr_post_profile(name, display_name, nip05, about)
- .await
- .map_err(Into::into)
- }
-
- pub async fn nostr_post_text_note(&self, content: String) -> Result<String, RadrootsAppError> {
- self.inner
- .nostr_post_text_note(content)
- .await
- .map_err(Into::into)
- }
-
- pub async fn nostr_fetch_text_notes(
- &self,
- limit: u16,
- since_unix: Option<u64>,
- ) -> Result<Vec<NostrPostEventMetadata>, RadrootsAppError> {
- self.inner
- .nostr_fetch_text_notes(limit, since_unix)
- .await
- .map_err(Into::into)
- }
-
- pub async fn nostr_post_reply(
- &self,
- parent_event_id_hex: String,
- parent_author_hex: String,
- content: String,
- root_event_id_hex: Option<String>,
- ) -> Result<String, RadrootsAppError> {
- self.inner
- .nostr_post_reply(
- parent_event_id_hex,
- parent_author_hex,
- content,
- root_event_id_hex,
- )
- .await
- .map_err(Into::into)
- }
-
pub fn phase1_card_types(&self) -> Vec<TodayCardType> {
self.inner.phase1_card_types()
}
diff --git a/core/crates/tera_ffi/tests/runtime_delegation.rs b/core/crates/tera_ffi/tests/runtime_delegation.rs
@@ -3,8 +3,6 @@ use radroots_mobile_ffi::RadrootsAppError;
mod support;
-const SECRET: &str = "0000000000000000000000000000000000000000000000000000000000000001";
-
#[tokio::test]
async fn native_boundary_delegates_the_complete_core_surface() {
let (_root, runtime) = support::runtime().await;
@@ -27,99 +25,6 @@ async fn native_boundary_delegates_the_complete_core_surface() {
"sqlite"
);
- assert!(!runtime.nostr_identity_has_selected_signing_identity());
- assert!(runtime.nostr_identity_selected_npub().is_none());
- assert!(
- runtime
- .nostr_identity_list()
- .expect("empty list")
- .is_empty()
- );
- assert!(
- runtime
- .nostr_identity_list_ids()
- .expect("empty identifiers")
- .is_empty()
- );
- assert!(
- runtime
- .nostr_identity_snapshot()
- .expect("empty snapshot")
- .identities
- .is_empty()
- );
- let validated = runtime
- .nostr_identity_validate_host_custody_secret(SECRET.to_owned())
- .expect("valid secret");
- let staged = runtime
- .nostr_identity_restore_host_custody_secret(
- SECRET.to_owned(),
- Some("staged".to_owned()),
- false,
- )
- .expect("staged identity");
- assert_eq!(validated.id, staged.id);
- let selected = runtime
- .nostr_identity_restore_host_custody_secret(
- SECRET.to_owned(),
- Some("selected".to_owned()),
- true,
- )
- .expect("selected identity");
- assert_eq!(
- runtime.nostr_identity_selected_npub(),
- Some(selected.public_key_npub.clone())
- );
- runtime
- .nostr_identity_select(selected.id.clone())
- .expect("select installed");
- assert!(runtime.nostr_identity_select("missing".to_owned()).is_err());
- runtime
- .nostr_identity_remove("missing".to_owned())
- .expect("removing missing identity is idempotent");
- runtime
- .nostr_identity_lock_host_custody_runtime()
- .expect("lock identity");
- runtime
- .nostr_identity_reset_host_custody_runtime()
- .expect("reset identity");
-
- assert!(runtime.nostr_set_default_relays(Vec::new()).is_err());
- assert!(runtime.nostr_connect_if_key_present().is_err());
- assert!(
- runtime
- .nostr_connection_status()
- .await
- .expect("unconfigured status")
- .last_error
- .is_none()
- );
- assert!(runtime.nostr_profile_for_self().await.is_err());
- assert!(
- runtime
- .nostr_post_profile(None, None, None, None)
- .await
- .is_err()
- );
- assert!(
- runtime
- .nostr_post_text_note("post".to_owned())
- .await
- .is_err()
- );
- assert!(runtime.nostr_fetch_text_notes(1, None).await.is_err());
- assert!(matches!(
- runtime
- .nostr_post_reply(
- "parent".to_owned(),
- "author".to_owned(),
- "reply".to_owned(),
- Some("different-root".to_owned()),
- )
- .await,
- Err(RadrootsAppError::Unsupported(_))
- ));
-
assert_eq!(
runtime.phase1_card_types(),
vec![