feat(server): let hosts ask whether a product is trusted for remote access - #802
filvecchiato wants to merge 1 commit into
Conversation
…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.
|
CI Status: 17 required jobs green, 15 passed and 2 skipped by path filter. All job results
Commit |
|
@lore-bot-app review |
|
Reading the diff and checking what the record says. Back in a few minutes. |
There was a problem hiding this comment.
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
Deniedstill revokes the grant. Thetruapi-platformREADME andpermissions.rs:257-260in 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
PermissionsServiceanswers, 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
-
Blocking. The export drops the "stored decision wins" invariant and the doc comment steers hosts into skipping it.
native.rs:386andwasm.rs:1137are 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 ofpeopl.dotnetwork access no longer applies on the host-mediated path. The core already exposes a stateful answer on both surfaces:permission_authorization_statusatnative.rs:1117andpermissionAuthorizationStatusatwasm.rs:1037runpeek_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'sgetPermissionStatealready orders it. -
Minor. Same body twice, tested once.
native.rs:387-388andwasm.rs:1138-1139are identical. Put the normalize-then-check intruapi_platformorhost_logicand call it from both. The wasm copy has no test today. -
Minor. Changeset names the wrong package and the TS surface is untyped. The wasm export ships in
@parity/truapi-host, not@parity/truapi. Thefixedgroup in.changeset/config.jsonbumps both, so versioning is fine, but the changelog entry lands under the wrong package.WasmModuleShapeinjs/packages/truapi-host/src/wasm-module.ts:77and the ambienttruapi_server.d.tsdo not declarehasTrustedRemotePermissions, so a TS host cannot call it without a cast. -
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 { |
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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.
Unblocks paritytech/platform-issues#4, where
peoplanddim2still 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_permissionswas 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, likeparseNavigate. It normalizes before matching, since a host holds ids in whatever spelling it received, and answersfalsefor 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-communityandpolkadot-android-community.cargo test -p truapi-servergreen; clippy--all-features,+nightly fmtandwasm32clean;make uniffiemits the binding. The test pins normalization, including thatapp.peopl.dotand a barepeoplare not trusted.