Skip to content

feat(server): let hosts ask whether a product is trusted for remote access - #802

Open
filvecchiato wants to merge 1 commit into
mainfrom
feat/export-trusted-remote-permissions
Open

filvecchiato wants to merge 1 commit into
mainfrom
feat/export-trusted-remote-permissions

Conversation

@filvecchiato

@filvecchiato filvecchiato commented Sep 16, 2026

Copy link
Copy Markdown
Collaborator

Unblocks paritytech/platform-issues#4, where peopl and dim2 still see permission prompts despite #446.

#446 governs the path the core owns: the permission request a product makes through the protocol. The prompts come from elsewhere — each host mediates product network access in its own code and decides there against its own store, never entering the core. And has_trusted_remote_permissions was exported on neither boundary, so a host could not consult it even deliberately.

hasTrustedRemotePermissions(productId) is now callable from UniFFI and wasm, so a host asks the core rather than keeping a second copy of the list. Pure and stateless, like parseNavigate. It normalizes before matching, since a host holds ids in whatever spelling it received, and answers false for an id that does not normalize.

Scope unchanged: remote permissions only. Device capabilities, identity disclosure and cross-product account access always prompt, so this removes no signing or payment confirmation.

Not trustedProducts: that field is inbound, what others may do to this product, while network access is outbound. A publisher declaring its own right to reach the internet is not a grant a host should honour.

Host-side follow-ups live in polkadot-ios-community and polkadot-android-community.

cargo test -p truapi-server green; clippy --all-features, +nightly fmt and wasm32 clean; make uniffi emits the binding. The test pins normalization, including that app.peopl.dot and a bare peopl are not trusted.

…ccess

The core grants every RemotePermission to a first-party product without
prompting, but only along the path it owns: the request a product makes through
the protocol. Each native host also mediates product network access in its own
code, in a webview interceptor or a fetch shim, and decides there against its
own store. That path never reaches the core, so a blessed product is still
stopped by the host for access the core would have granted.

has_trusted_remote_permissions is now callable from both boundaries, so a host
can ask rather than keep a second copy of the list. It normalizes before
matching, because a host holds ids in whatever spelling it received, and answers
false for an id that does not normalize so an unknown spelling is never read as
trusted.

Remote permissions only, unchanged: device capabilities, identity disclosure and
cross-product account access always prompt, whoever asks.
@filvecchiato
filvecchiato requested a review from a team September 16, 2026 11:47
@github-actions

Copy link
Copy Markdown

CI Status: 17 required jobs green, 15 passed and 2 skipped by path filter.

All job results
job result
android-bindings success
changes success
changeset-guard success
codegen success
e2e skipped
explorer success
ios-bindings success
ios-swift success
licenses success
playground success
release-guard success
rust success
ts-client success
ts-debugger success
ts-host success
wasm-provider success
workflow-lint skipped

Commit 34f3774f · run log

@TarikGul

Copy link
Copy Markdown
Member

@lore-bot-app review

@lore-bot-app

lore-bot-app Bot commented Sep 16, 2026

Copy link
Copy Markdown

Reading the diff and checking what the record says. Back in a few minutes.

@lore-bot-app lore-bot-app Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TL;DR
Exports the compiled-in first-party trust check (peopl, dim2, stash) to UniFFI and wasm so hosts can skip their own network prompt for blessed products. 1 blocking, 3 minor.

Summary
Adds has_trusted_remote_permissions(product_id) as a #[uniffi::export] free function in native.rs and as hasTrustedRemotePermissions in wasm.rs. Both normalize the id, answer false when normalization fails, and delegate to the existing truapi_platform::has_trusted_remote_permissions. A native-side test covers spelling variants. A changeset bumps @parity/truapi minor. No host wiring is included; the intended consumers are the iOS and Android webview interceptors.

What the record says

  • The trusted-label list landed in #446 with the explicit invariant that a stored Denied still revokes the grant. The truapi-platform README and permissions.rs:257-260 in this checkout restate it: a stored decision always wins.
  • #372 documented the gap this PR targets: domain permissions bypass the core on Android and iOS and rely on host-side WebView enforcement, with inconsistent coverage. The recommendation was per-platform native enforcement.
  • #794 and #793 (pgherveou, active Sep 16) take a different route to the same gap: the native and container fetch gates connect to the shared Rust permission service, so the core's PermissionsService answers, trusted list included. Both are unresolved.
  • #596 (REQ6) is the requirement behind blessing. It asks for dynamic updates without rebuilding the host, and was absorbed into #624. A compiled-in list exported over the host ABI moves away from that.
  • The PR itself is #802. The record has no discussion of it beyond the description.

Concerns

  1. Blocking. The export drops the "stored decision wins" invariant and the doc comment steers hosts into skipping it. native.rs:386 and wasm.rs:1137 are pure. A host that follows the comment ("has to ask here too, or a blessed product is prompted") will consult it before any stored decision, so a user's revocation of peopl.dot network access no longer applies on the host-mediated path. The core already exposes a stateful answer on both surfaces: permission_authorization_status at native.rs:1117 and permissionAuthorizationStatus at wasm.rs:1037 run peek_remote, which folds in the trusted list and stored decisions. Either point hosts at that, or state in both doc comments that this is only for the branch where the host's own store reads undetermined, as the iOS handler's getPermissionState already orders it.

  2. Minor. Same body twice, tested once. native.rs:387-388 and wasm.rs:1138-1139 are identical. Put the normalize-then-check in truapi_platform or host_logic and call it from both. The wasm copy has no test today.

  3. Minor. Changeset names the wrong package and the TS surface is untyped. The wasm export ships in @parity/truapi-host, not @parity/truapi. The fixed group in .changeset/config.json bumps both, so versioning is fine, but the changelog entry lands under the wrong package. WasmModuleShape in js/packages/truapi-host/src/wasm-module.ts:77 and the ambient truapi_server.d.ts do not declare hasTrustedRemotePermissions, so a TS host cannot call it without a cast.

  4. Minor. Doc comment says "Pure and stateless" only on the native side. The wasm comment omits it. Whichever resolution of concern 1 you pick, keep the two comments identical.

Questions for the author

  • How does this relate to #794 and #793? If the native fetch gate ends up calling the shared Rust permission service, the host never needs this export. Is #802 a stopgap until that lands, or the intended long-term path?
  • Where in the iOS and Android handlers will this be called? Before or after the host's own stored decision? The answer decides whether concern 1 is a doc fix or a design problem.
  • REQ6 (#596) asked for a list that updates without a host rebuild. Does exporting the compiled-in list to the host ABI close that door, or is there a plan to source it from chain or manifest later?

Next: decide whether to route hosts through permissionAuthorizationStatus or add the ordering sentence to both doc comments. Either is a 10-minute change.


🤖 Reviewed by Lore (Parity knowledge base) · 38 agent turns · 276.3s · knowledge as of 2026-09-16

/// normalize, so an unknown spelling is never read as trusted. Pure and
/// stateless.
#[uniffi::export]
pub fn has_trusted_remote_permissions(product_id: String) -> bool {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocking. The export drops the "stored decision wins" invariant and the doc comment steers hosts into skipping it. native.rs:386 and wasm.rs:1137 are pure. A host that follows the comment ("has to ask here too, or a blessed product is prompted") will consult it before any stored decision, so a user's revocation of peopl.dot network access no longer applies on the host-mediated path. The core already exposes a stateful answer on both surfaces: permission_authorization_status at native.rs:1117 and permissionAuthorizationStatus at wasm.rs:1037 run peek_remote, which folds in the trusted list and stored decisions. Either point hosts at that, or state in both doc comments that this is only for the branch where the host's own store reads undetermined, as the iOS handler's getPermissionState already orders it.

/// stateless.
#[uniffi::export]
pub fn has_trusted_remote_permissions(product_id: String) -> bool {
truapi_platform::normalize_product_identifier(&product_id)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor. Same body twice, tested once. native.rs:387-388 and wasm.rs:1138-1139 are identical. Put the normalize-then-check in truapi_platform or host_logic and call it from both. The wasm copy has no test today.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants