app

Local-first trade for farms and co-ops
git clone https://radroots.dev/git/app.git
Log | Files | Refs | README | LICENSE

commit 4cb986daef8d642f315a236a0a7a511990d0121f
parent 8df4791632b9a206d2a28fa4fc6d1bd718c68b01
Author: triesap <tyson@radroots.org>
Date:   Thu, 18 Jun 2026 14:35:39 -0700

app: expand sdk boundary source guards

- scan app production Rust sources with the full strict SDK boundary pattern set
- require owner, reason, and removal condition for remaining legacy SDK boundary exceptions
- add raw-key custody allowlist entries for current account and signer bootstrap paths
- cover new unallowlisted production paths with a focused source guard regression

Diffstat:
Mcrates/desktop/src/source_guards.rs | 191++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
1 file changed, 107 insertions(+), 84 deletions(-)

diff --git a/crates/desktop/src/source_guards.rs b/crates/desktop/src/source_guards.rs @@ -19,6 +19,7 @@ const ALLOWED_WINDOW_LITERALS: &[&str] = &[ "127.0.0.1", "14", "14.5", + "2", "2 bags", "2222222222222222222222222222222222222222222222222222222222222222", "3333333333333333333333333333333333333333333333333333333333333333", @@ -35,6 +36,7 @@ const ALLOWED_WINDOW_LITERALS: &[&str] = &[ "USD", "[::1]", "/tmp/radroots/data/apps/app", + "/tmp/radroots/data/apps/app/sdk", "/tmp/radroots/logs/apps/app", "{}.{:02}", "abc", @@ -111,11 +113,15 @@ const ALLOWED_WINDOW_LITERALS: &[&str] = &[ "buyer.search_query_update_failed", "CARGO_PKG_VERSION", "clock", + "configuration", + "configure_relay_targets", "customer_labels.txt", "desktop runtime paths should resolve", "desktop runtime roots require HOME for macos", + "directory", "disk unavailable", "eggs", + "event_store.sqlite", "failed to add buyer product to cart", "failed to open buyer order detail", "failed to place buyer order", @@ -267,6 +273,7 @@ const ALLOWED_WINDOW_LITERALS: &[&str] = &[ "orders.recovery_review_failed", "orders.recovery_start_failed", "orders.route_failed", + "outbox.sqlite", "preview", "pack_sheet.txt", "pack_sheet.txt, pickup_roster.txt, customer_labels.txt", @@ -316,11 +323,15 @@ const ALLOWED_WINDOW_LITERALS: &[&str] = &[ "invalid discovery url: relative URL without a base", "invalid remote signer uri:", "invalid remote signer uri: invalid public key", + "invalid_relay_url", "a remote signer connection is already pending approval", "raw nostrconnect client uris are signer-side only", "remote signer", "remote signer connection failed: relay refused the request", "remote signer did not respond yet", + "retry_startup", + "retry_status_refresh", + "review_runtime_configuration", "runtime unavailable", "radroots_home_view_{label}_{suffix}", "sign_event:kind:1", @@ -330,6 +341,7 @@ const ALLOWED_WINDOW_LITERALS: &[&str] = &[ "shell-mode-marketplace", "shell.switch_farm_failed", "shell.switch_marketplace_failed", + "sdk_canonical_stores", "settings", "settings-add-blackout-period", "settings-add-fulfillment-window", @@ -364,6 +376,7 @@ const ALLOWED_WINDOW_LITERALS: &[&str] = &[ "switch_relays", "startup-title-radroots", "startup-title-starting", + "wait_for_sdk_lifecycle", "ws://localhost:8080", "ws://localhost:8081", "wss://relay.example", @@ -1178,51 +1191,46 @@ struct LegacySdkBoundaryAllowlistEntry { const TEST_MODULE_SENTINEL: &str = "\n#[cfg(test)]\nmod tests {"; -const SDK_MIGRATED_SOURCE_PATHS: &[&str] = &[ - "crates/runtime/src/sdk.rs", - "crates/store/src/migration_audit.rs", -]; - -const FORBIDDEN_SDK_MIGRATED_BOUNDARY_PATTERNS: &[SdkBoundaryForbiddenPattern] = &[ +const STRICT_SDK_BOUNDARY_FORBIDDEN_PATTERNS: &[SdkBoundaryForbiddenPattern] = &[ SdkBoundaryForbiddenPattern { pattern: "SdkDirectRelayAppSyncTransport", - reason: "migrated paths must use AppSdkRuntime instead of the legacy direct relay sync transport", + reason: "app production sources must use AppSdkRuntime instead of the legacy direct relay sync transport", }, SdkBoundaryForbiddenPattern { pattern: "RadrootsSdkClient", - reason: "migrated paths must use the long-lived RadrootsSdk runtime boundary", + reason: "app production sources must use the long-lived RadrootsSdk runtime boundary", }, SdkBoundaryForbiddenPattern { pattern: "RadrootsSdkConfig", - reason: "migrated paths must use AppSdkConfig-derived runtime construction", + reason: "app production sources must use AppSdkConfig-derived runtime construction", }, SdkBoundaryForbiddenPattern { pattern: "SdkTransportMode::RelayDirect", - reason: "migrated paths must not configure direct relay publish transport", + reason: "app production sources must not configure direct relay publish transport", }, SdkBoundaryForbiddenPattern { pattern: "SignerConfig::LocalIdentity", - reason: "migrated paths must not configure local direct-publish signing", + reason: "app production sources must not configure local direct-publish signing", }, SdkBoundaryForbiddenPattern { pattern: "PendingSyncOperation::from_publish_payload", - reason: "migrated paths must not enqueue legacy app publish payloads", + reason: "app production sources must not enqueue legacy app publish payloads", }, SdkBoundaryForbiddenPattern { pattern: ".enqueue_pending_operation(", - reason: "migrated paths must not mutate the legacy app outbox", + reason: "app production sources must not mutate the legacy app outbox", }, SdkBoundaryForbiddenPattern { pattern: "INSERT INTO local_outbox", - reason: "migrated paths must not write legacy local outbox rows", + reason: "app production sources must not write legacy local outbox rows", }, SdkBoundaryForbiddenPattern { pattern: "UPDATE local_outbox", - reason: "migrated paths must not mutate legacy local outbox rows", + reason: "app production sources must not mutate legacy local outbox rows", }, SdkBoundaryForbiddenPattern { pattern: "DELETE FROM local_outbox", - reason: "migrated paths must not delete legacy local outbox rows", + reason: "app production sources must not delete legacy local outbox rows", }, SdkBoundaryForbiddenPattern { pattern: "RadrootsOutbox", @@ -1238,88 +1246,66 @@ const FORBIDDEN_SDK_MIGRATED_BOUNDARY_PATTERNS: &[SdkBoundaryForbiddenPattern] = }, SdkBoundaryForbiddenPattern { pattern: "connected_client_from_identity", - reason: "migrated paths must not connect relay clients directly for publish", + reason: "app production sources must not connect relay clients directly for publish", }, SdkBoundaryForbiddenPattern { pattern: "publish_signed_event", - reason: "migrated paths must not publish signed events directly", + reason: "app production sources must not publish signed events directly", }, SdkBoundaryForbiddenPattern { pattern: "radroots_nostr_build_event", - reason: "migrated paths must not build protocol events outside SDK-owned publish APIs", + reason: "app production sources must not build protocol events outside SDK-owned publish APIs", }, SdkBoundaryForbiddenPattern { pattern: "RadrootsIdentity::from_secret_key_str", - reason: "migrated paths must not parse direct signing keys", + reason: "app production sources must not parse direct signing keys", }, SdkBoundaryForbiddenPattern { pattern: "RawSecretKey", - reason: "migrated paths must not import raw signing-key material", + reason: "app production sources must not import raw signing-key material", }, SdkBoundaryForbiddenPattern { pattern: "EncryptedSecretKey", - reason: "migrated paths must not import encrypted signing-key material", + reason: "app production sources must not import encrypted signing-key material", }, SdkBoundaryForbiddenPattern { pattern: "publish_with_identity", - reason: "migrated paths must not call legacy direct SDK publish APIs", + reason: "app production sources must not call legacy direct SDK publish APIs", }, SdkBoundaryForbiddenPattern { pattern: "publish_draft_with_identity", - reason: "migrated paths must not encode legacy direct SDK publish targets", + reason: "app production sources must not encode legacy direct SDK publish targets", }, SdkBoundaryForbiddenPattern { pattern: "publish_order_request_with_identity", - reason: "migrated paths must not call legacy direct SDK order publish APIs", + reason: "app production sources must not call legacy direct SDK order publish APIs", }, SdkBoundaryForbiddenPattern { pattern: "publish_order_decision_with_identity", - reason: "migrated paths must not call legacy direct SDK order publish APIs", + reason: "app production sources must not call legacy direct SDK order publish APIs", }, SdkBoundaryForbiddenPattern { pattern: "publish_order_revision_proposal_with_identity", - reason: "migrated paths must not call legacy direct SDK order publish APIs", + reason: "app production sources must not call legacy direct SDK order publish APIs", }, SdkBoundaryForbiddenPattern { pattern: "publish_order_revision_decision_with_identity", - reason: "migrated paths must not call legacy direct SDK order publish APIs", + reason: "app production sources must not call legacy direct SDK order publish APIs", }, SdkBoundaryForbiddenPattern { pattern: "publish_order_cancellation_with_identity", - reason: "migrated paths must not call legacy direct SDK order publish APIs", + reason: "app production sources must not call legacy direct SDK order publish APIs", }, SdkBoundaryForbiddenPattern { pattern: "publish_fulfillment_update_with_identity", - reason: "migrated paths must not call legacy direct SDK fulfillment publish APIs", + reason: "app production sources must not call legacy direct SDK fulfillment publish APIs", }, SdkBoundaryForbiddenPattern { pattern: "publish_buyer_receipt_with_identity", - reason: "migrated paths must not call legacy direct SDK receipt publish APIs", + reason: "app production sources must not call legacy direct SDK receipt publish APIs", }, ]; -const LEGACY_SDK_BOUNDARY_PATTERNS: &[&str] = &[ - "SdkDirectRelayAppSyncTransport", - "RadrootsSdkClient", - "RadrootsSdkConfig", - "SdkTransportMode::RelayDirect", - "SignerConfig::LocalIdentity", - "PendingSyncOperation::from_publish_payload", - ".enqueue_pending_operation(", - "INSERT INTO local_outbox", - "UPDATE local_outbox", - "DELETE FROM local_outbox", - "publish_with_identity", - "publish_draft_with_identity", - "publish_order_request_with_identity", - "publish_order_decision_with_identity", - "publish_order_revision_proposal_with_identity", - "publish_order_revision_decision_with_identity", - "publish_order_cancellation_with_identity", - "publish_fulfillment_update_with_identity", - "publish_buyer_receipt_with_identity", -]; - const LEGACY_SDK_BOUNDARY_ALLOWLIST: &[LegacySdkBoundaryAllowlistEntry] = &[ LegacySdkBoundaryAllowlistEntry { path: "crates/desktop/src/runtime.rs", @@ -1420,6 +1406,34 @@ const LEGACY_SDK_BOUNDARY_ALLOWLIST: &[LegacySdkBoundaryAllowlistEntry] = &[ removal_condition: "remove when buyer receipt workflow enqueues through AppSdkRuntime", }, LegacySdkBoundaryAllowlistEntry { + path: "crates/desktop/src/accounts.rs", + pattern: "RadrootsIdentity::from_secret_key_str", + owner: "rpv1-app-sdk-hardening.04", + reason: "desktop account import still accepts local raw secret-key material for account bootstrap", + removal_condition: "remove when local account import is mediated by protected signer adapters instead of raw key parsing", + }, + LegacySdkBoundaryAllowlistEntry { + path: "crates/desktop/src/accounts.rs", + pattern: "RawSecretKey", + owner: "rpv1-app-sdk-hardening.04", + reason: "desktop account import still exposes a raw secret-key import mode for account bootstrap", + removal_condition: "remove when local account import is mediated by protected signer adapters instead of raw key import modes", + }, + LegacySdkBoundaryAllowlistEntry { + path: "crates/desktop/src/accounts.rs", + pattern: "EncryptedSecretKey", + owner: "rpv1-app-sdk-hardening.04", + reason: "desktop account import still exposes an encrypted secret-key import mode for account bootstrap", + removal_condition: "remove when local account import is mediated by protected signer adapters instead of secret-key import modes", + }, + LegacySdkBoundaryAllowlistEntry { + path: "crates/signer/src/protocol.rs", + pattern: "RadrootsIdentity::from_secret_key_str", + owner: "rpv1-app-sdk-hardening.04", + reason: "remote signer protocol connection still materializes client identity from local pending-session custody", + removal_condition: "remove when remote signer protocol sessions are mediated by SDK signer adapters and protected store APIs", + }, + LegacySdkBoundaryAllowlistEntry { path: "crates/sync/src/publish.rs", pattern: "SdkTransportMode::RelayDirect", owner: "rpv1-app-sdk-refactor.07", @@ -1675,26 +1689,41 @@ fn app_production_trade_event_kinds_use_shared_constants() { } #[test] -fn app_migrated_sdk_paths_do_not_use_direct_publish_boundaries() { - let app_root = app_root(); - - for relative_path in SDK_MIGRATED_SOURCE_PATHS { - let path = app_root.join(relative_path); - let source = read_source_path(path.as_path()); +fn app_production_sdk_boundary_usage_is_allowlisted() { + for (relative_path, source) in app_rust_source_files() { let production_source = production_source_without_tests(&source); + let findings = + unallowlisted_sdk_boundary_patterns(relative_path.as_str(), production_source); - for forbidden in FORBIDDEN_SDK_MIGRATED_BOUNDARY_PATTERNS { - assert!( - !production_source.contains(forbidden.pattern), - "{relative_path} contains forbidden SDK boundary pattern `{}`: {}", - forbidden.pattern, - forbidden.reason - ); - } + assert!( + findings.is_empty(), + "{} contains unallowlisted SDK boundary pattern `{}`: {}", + relative_path, + findings.first().map_or("", |finding| finding.pattern), + findings.first().map_or("", |finding| finding.reason) + ); } } #[test] +fn strict_sdk_boundary_scanner_rejects_unallowlisted_new_production_paths() { + let findings = unallowlisted_sdk_boundary_patterns( + "crates/desktop/src/new_workflow.rs", + "fn publish() { let _ = RadrootsSdkClient::from_config(config); }", + ); + + assert_eq!(findings.len(), 1); + assert_eq!(findings[0].pattern, "RadrootsSdkClient"); + assert!( + unallowlisted_sdk_boundary_patterns( + "crates/desktop/src/runtime.rs", + "fn publish() { let _ = RadrootsSdkClient::from_config(config); }", + ) + .is_empty() + ); +} + +#[test] fn app_legacy_sdk_boundary_allowlist_entries_are_complete_and_current() { let app_root = app_root(); let mut entries = BTreeSet::new(); @@ -1737,23 +1766,6 @@ fn app_legacy_sdk_boundary_allowlist_entries_are_complete_and_current() { } } -#[test] -fn app_legacy_sdk_boundary_usage_is_allowlisted() { - for (relative_path, source) in app_rust_source_files() { - let production_source = production_source_without_tests(&source); - - for pattern in LEGACY_SDK_BOUNDARY_PATTERNS { - if production_source.contains(pattern) { - assert!( - legacy_sdk_boundary_allowlist_contains(relative_path.as_str(), pattern), - "{} contains unallowlisted legacy SDK boundary pattern `{pattern}`", - relative_path - ); - } - } - } -} - fn extract_string_literals(source: &str) -> BTreeSet<&str> { let mut literals = BTreeSet::new(); let bytes = source.as_bytes(); @@ -1792,6 +1804,17 @@ fn production_source_without_tests(source: &str) -> &str { .map_or(source, |(production_source, _)| production_source) } +fn unallowlisted_sdk_boundary_patterns( + path: &str, + production_source: &str, +) -> Vec<&'static SdkBoundaryForbiddenPattern> { + STRICT_SDK_BOUNDARY_FORBIDDEN_PATTERNS + .iter() + .filter(|forbidden| production_source.contains(forbidden.pattern)) + .filter(|forbidden| !legacy_sdk_boundary_allowlist_contains(path, forbidden.pattern)) + .collect() +} + fn legacy_sdk_boundary_allowlist_contains(path: &str, pattern: &str) -> bool { LEGACY_SDK_BOUNDARY_ALLOWLIST .iter()