From fd47c43a68ec92b8aabb0384ca9666fe04188bc6 Mon Sep 17 00:00:00 2001 From: Filippo Vecchiato Date: Mon, 22 Jun 2026 11:40:10 +0200 Subject: [PATCH 1/2] feat: permission annotations in Rust doc comments and codegen MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Embed `# Permissions` sections in API trait method doc comments so hosts can extract permission requirements directly from the rustdoc JSON output. The codegen pipeline parses these sections and emits `permissions` metadata in the generated TypeScript playground/explorer service descriptors. Methods with no permission requirements carry no `# Permissions` section at all. Annotated methods declare a combination of: - **auth**: whether authentication is required - **prompt**: user-facing confirmation dialog - **permission**: named permission gate (RemotePermission/DevicePermission) - **denial_error**: error variant returned on denial Includes `docs/host-permission-requirements.md` — the canonical permission matrix for all TrUAPI methods across tiers. --- docs/host-permission-requirements.md | 212 ++++++++++++++++++ .../truapi/src/playground/services-types.ts | 9 + .../truapi-codegen/src/ts/playground.rs | 86 +++++++ rust/crates/truapi/src/api/account.rs | 22 ++ rust/crates/truapi/src/api/chain.rs | 8 + rust/crates/truapi/src/api/chat.rs | 26 +++ rust/crates/truapi/src/api/entropy.rs | 4 + rust/crates/truapi/src/api/notifications.rs | 8 + rust/crates/truapi/src/api/payment.rs | 21 ++ rust/crates/truapi/src/api/permissions.rs | 8 + rust/crates/truapi/src/api/preimage.rs | 4 + .../truapi/src/api/resource_allocation.rs | 5 + rust/crates/truapi/src/api/signing.rs | 36 +++ rust/crates/truapi/src/api/statement_store.rs | 14 ++ rust/crates/truapi/src/api/system.rs | 4 + 15 files changed, 467 insertions(+) create mode 100644 docs/host-permission-requirements.md diff --git a/docs/host-permission-requirements.md b/docs/host-permission-requirements.md new file mode 100644 index 00000000..b8daa5d0 --- /dev/null +++ b/docs/host-permission-requirements.md @@ -0,0 +1,212 @@ +# TrUAPI Host Permission Requirements + +Every TrUAPI method falls into one of four permission tiers. Hosts **must** enforce the requirements listed below before executing each call. + +## Legend + +| Column | Meaning | +|--------|---------| +| **Auth** | User must be logged in (`NotConnected` error if not) | +| **Prompt** | Host must show a user-facing confirmation UI before proceeding | +| **Permission type** | Which permission system governs the prompt | + +--- + +## 1. No permission required + +These methods work without login and without any user prompt. + +| Method | Notes | +|--------|-------| +| `host_handshake` | Protocol negotiation | +| `host_feature_supported` | Capability query | +| `host_local_storage_read` | Product-scoped storage | +| `host_local_storage_write` | Product-scoped storage | +| `host_local_storage_clear` | Product-scoped storage | +| `host_theme_subscribe` | UI theming | +| `host_account_connection_status_subscribe` | Read-only status | +| `host_request_login` | Presents login UI (user controls outcome) | +| `remote_chain_head_follow_subscribe` | Read-only chain data | +| `remote_chain_head_header` | Read-only chain data | +| `remote_chain_head_body` | Read-only chain data | +| `remote_chain_head_storage` | Read-only chain data | +| `remote_chain_head_call` | Read-only chain data | +| `remote_chain_head_unpin` | Read-only chain data | +| `remote_chain_head_continue` | Read-only chain data | +| `remote_chain_head_stop_operation` | Read-only chain data | +| `remote_chain_spec_genesis_hash` | Read-only chain data | +| `remote_chain_spec_chain_name` | Read-only chain data | +| `remote_chain_spec_properties` | Read-only chain data | +| `remote_statement_store_subscribe` | Read-only statement data | +| `remote_preimage_lookup_subscribe` | Read-only preimage data | + +--- + +## 2. Authentication required (no additional prompt) + +User must be logged in. The host does **not** show a separate permission prompt — access is granted to any authenticated product. + +| Method | Error on no auth | +|--------|------------------| +| `host_account_get` | `NotConnected` / `Rejected` | +| `host_account_get_alias` | `NotConnected` / `Rejected` | +| `host_account_create_proof` | `NotConnected` / `Rejected` | +| `host_derive_entropy` | `Unknown` | +| `host_get_legacy_accounts` | `Rejected` | +| `host_chat_list_subscribe` | Requires active session | +| `host_chat_action_subscribe` | Requires active session | +| `product_chat_custom_message_render_subscribe` | Requires active session | +| `host_payment_status_subscribe` | `PaymentNotFound` | + +--- + +## 3. Authentication + user confirmation prompt + +These methods require login **and** an explicit user-facing prompt before proceeding. The host must present a confirmation UI and return `Rejected` / `PermissionDenied` / `Denied` if the user declines. + +### 3a. Signing & transaction confirmation + +The host shows the user what is being signed or submitted, and the user approves or rejects. + +| Method | Prompt trigger | Error on denial | +|--------|---------------|-----------------| +| `host_create_transaction` | Always — user reviews transaction details | `Rejected` / `PermissionDenied` | +| `host_create_transaction_with_legacy_account` | Always — user reviews transaction details | `Rejected` / `PermissionDenied` | +| `host_sign_raw` | Always — user reviews payload | `Rejected` / `PermissionDenied` | +| `host_sign_payload` | Always — user reviews extrinsic payload | `Rejected` / `PermissionDenied` | +| `host_sign_raw_with_legacy_account` | Always — user reviews payload | `Rejected` / `PermissionDenied` | +| `host_sign_payload_with_legacy_account` | Always — user reviews extrinsic payload | `Rejected` / `PermissionDenied` | + +### 3b. Identity disclosure + +| Method | Prompt trigger | Error on denial | +|--------|---------------|-----------------| +| `host_get_user_id` | Always — user approves revealing their primary DotNS name to the product | `PermissionDenied` | + +### 3c. Payment confirmation + +| Method | Prompt trigger | Error on denial | +|--------|---------------|-----------------| +| `host_payment_balance_subscribe` | First call — user approves balance disclosure | `PermissionDenied` | +| `host_payment_request` | Always — user approves spend | `Rejected` | +| `host_payment_top_up` | Depends on source — host may prompt for `ProductAccount` source | `InsufficientFunds` / `InvalidSource` | + +### 3d. Chat room & bot registration + +| Method | Prompt trigger | Error on denial | +|--------|---------------|-----------------| +| `host_chat_create_room` | Host may prompt on first room creation | `PermissionDenied` | +| `host_chat_register_bot` | Host may prompt on first bot registration | `PermissionDenied` | +| `host_chat_post_message` | No prompt (already authorized by room creation) | `MessageTooLarge` | + +### 3e. Statement store proof creation + +| Method | Prompt trigger | Error on denial | +|--------|---------------|-----------------| +| ~~`remote_statement_store_create_proof`~~ | **Deprecated** — use `create_proof_authorized` instead | — | +| `remote_statement_store_create_proof_authorized` | No per-call prompt — uses pre-allocated `AutoSigning` allowance from `host_request_resource_allocation` | `UnableToSign` | + +--- + +## 4. Device & remote permissions (RFC 0002) + +These permissions use the RFC 0002 permission model: the host prompts once, persists the user's decision indefinitely, and does not re-prompt on subsequent requests. + +### 4a. Explicit permission requests + +Products may pre-request permissions; the host shows a one-time prompt. + +| Method | Permission | +|--------|------------| +| `host_device_permission` | Requests one `DevicePermission` variant | +| `remote_permission` | Requests one `RemotePermission` variant | + +**`DevicePermission` variants:** `Notifications`, `Camera`, `Microphone`, `Bluetooth`, `NFC`, `Location`, `Clipboard`, `OpenUrl`, `Biometrics` + +**`RemotePermission` variants:** `Remote { domains }`, `WebRtc`, `ChainSubmit`, `PreimageSubmit`, `StatementSubmit` + +### 4b. Implicit permission triggers + +These business methods **automatically trigger** a remote permission prompt if the corresponding permission has not been granted yet. The host should prompt for the permission before executing the call. + +| Method | Implicitly requires | Error on denial | +|--------|-------------------|-----------------| +| `host_navigate_to` | `DevicePermission::OpenUrl` | `PermissionDenied` | +| `host_push_notification` | `DevicePermission::Notifications` | `Unknown` | +| `host_push_notification_cancel` | `DevicePermission::Notifications` (same grant) | `Unknown` | +| `remote_chain_transaction_broadcast` | `RemotePermission::ChainSubmit` | `GenericError` | +| `remote_chain_transaction_stop` | `RemotePermission::ChainSubmit` (same grant) | `GenericError` | +| `remote_preimage_submit` | `RemotePermission::PreimageSubmit` | `GenericError` | +| `remote_statement_store_submit` | `RemotePermission::StatementSubmit` | `GenericError` | + +### 4c. Resource allocation + +Pre-allocates capabilities that relax per-call prompts for subsequent operations. + +| Method | Notes | +|--------|-------| +| `host_request_resource_allocation` | User approves each `AllocatableResource`. Grants like `AutoSigning` enable `create_proof_authorized` without per-call prompts. Per-resource outcome: `Allocated` / `Rejected` / `NotAvailable`. | + +**`AllocatableResource` variants:** `StatementStoreAllowance`, `BulletinAllowance`, `SmartContractAllowance`, `AutoSigning` + +--- + +## Quick reference matrix + +| Method | Auth | Prompt | Permission type | +|--------|:----:|:------:|----------------| +| `host_handshake` | | | — | +| `host_feature_supported` | | | — | +| `host_push_notification` | | | DevicePermission::Notifications | +| `host_push_notification_cancel` | | | DevicePermission::Notifications | +| `host_navigate_to` | | | DevicePermission::OpenUrl | +| `host_device_permission` | | | Explicit prompt | +| `remote_permission` | | | Explicit prompt | +| `host_local_storage_read` | | | — | +| `host_local_storage_write` | | | — | +| `host_local_storage_clear` | | | — | +| `host_account_connection_status_subscribe` | | | — | +| `host_account_get` | * | | — | +| `host_account_get_alias` | * | | — | +| `host_account_create_proof` | * | | — | +| `host_get_legacy_accounts` | * | | — | +| `host_create_transaction` | * | * | Signing confirmation | +| `host_create_transaction_with_legacy_account` | * | * | Signing confirmation | +| `host_sign_raw_with_legacy_account` | * | * | Signing confirmation | +| `host_sign_payload_with_legacy_account` | * | * | Signing confirmation | +| `host_chat_create_room` | * | * | Chat registration | +| `host_chat_register_bot` | * | * | Chat registration | +| `host_chat_list_subscribe` | * | | — | +| `host_chat_post_message` | * | | — | +| `host_chat_action_subscribe` | * | | — | +| `product_chat_custom_message_render_subscribe` | * | | — | +| `remote_statement_store_subscribe` | | | — | +| ~~`remote_statement_store_create_proof`~~ | * | * | **Deprecated** | +| `remote_statement_store_create_proof_authorized` | * | | Pre-allocated allowance | +| `remote_statement_store_submit` | | | RemotePermission::StatementSubmit | +| `remote_preimage_lookup_subscribe` | | | — | +| `remote_preimage_submit` | | | RemotePermission::PreimageSubmit | +| `remote_chain_head_follow_subscribe` | | | — | +| `remote_chain_head_header` | | | — | +| `remote_chain_head_body` | | | — | +| `remote_chain_head_storage` | | | — | +| `remote_chain_head_call` | | | — | +| `remote_chain_head_unpin` | | | — | +| `remote_chain_head_continue` | | | — | +| `remote_chain_head_stop_operation` | | | — | +| `remote_chain_spec_genesis_hash` | | | — | +| `remote_chain_spec_chain_name` | | | — | +| `remote_chain_spec_properties` | | | — | +| `remote_chain_transaction_broadcast` | | | RemotePermission::ChainSubmit | +| `remote_chain_transaction_stop` | | | RemotePermission::ChainSubmit | +| `host_theme_subscribe` | | | — | +| `host_derive_entropy` | * | | — | +| `host_get_user_id` | * | * | Identity disclosure | +| `host_request_login` | | | — (presents login UI) | +| `host_sign_raw` | * | * | Signing confirmation | +| `host_sign_payload` | * | * | Signing confirmation | +| `host_payment_balance_subscribe` | * | * | Balance disclosure | +| `host_payment_top_up` | * | | Source-dependent | +| `host_payment_request` | * | * | Payment confirmation | +| `host_payment_status_subscribe` | * | | — | +| `host_request_resource_allocation` | * | * | Per-resource prompt | diff --git a/js/packages/truapi/src/playground/services-types.ts b/js/packages/truapi/src/playground/services-types.ts index cf62fce6..5d1e9bb9 100644 --- a/js/packages/truapi/src/playground/services-types.ts +++ b/js/packages/truapi/src/playground/services-types.ts @@ -14,6 +14,15 @@ export interface MethodInfo { responseType?: string; /** DataType id of the method's error. */ errorType?: string; + /** Permission requirements for the method. Absent means no permissions needed. */ + permissions?: MethodPermissions; +} + +export interface MethodPermissions { + auth?: string; + prompt?: string; + permissionType?: string; + denialError?: string; } export interface ServiceInfo { diff --git a/rust/crates/truapi-codegen/src/ts/playground.rs b/rust/crates/truapi-codegen/src/ts/playground.rs index 51de54ff..54dd3214 100644 --- a/rust/crates/truapi-codegen/src/ts/playground.rs +++ b/rust/crates/truapi-codegen/src/ts/playground.rs @@ -136,6 +136,9 @@ fn generate_playground_services_code( { writeln!(out, " errorType: {},", ts_string_literal(&id)).unwrap(); } + if let Some(perms) = &docs.permissions { + emit_permissions(&mut out, perms); + } writeln!(out, " }},").unwrap(); } @@ -158,6 +161,16 @@ fn generate_playground_services_code( pub(super) struct PlaygroundDocs { pub(super) description: Option, pub(super) client_example: Option, + pub(super) permissions: Option, +} + +/// Structured permission requirements extracted from a `# Permissions` doc section. +#[derive(Debug)] +pub(super) struct MethodPermissions { + pub(super) auth: Option, + pub(super) prompt: Option, + pub(super) permission_type: Option, + pub(super) denial_error: Option, } pub(super) fn split_playground_docs(docs: Option<&str>) -> Result { @@ -165,16 +178,20 @@ pub(super) fn split_playground_docs(docs: Option<&str>) -> Result) -> Result) -> Result Option { + let mut auth = None; + let mut prompt = None; + let mut permission_type = None; + let mut denial_error = None; + + for line in lines { + let Some(rest) = line.strip_prefix("- **") else { + continue; + }; + let Some((key, value)) = rest.split_once("**:") else { + continue; + }; + let value = value.trim(); + if value.is_empty() { + continue; + } + match key { + "auth" => auth = Some(value.to_string()), + "prompt" => prompt = Some(value.to_string()), + "permission" => permission_type = Some(value.to_string()), + "denial_error" => denial_error = Some(value.to_string()), + _ => {} + } + } + + if auth.is_none() && prompt.is_none() && permission_type.is_none() && denial_error.is_none() { + return None; + } + + Some(MethodPermissions { + auth, + prompt, + permission_type, + denial_error, }) } @@ -238,6 +307,23 @@ fn validate_example_docs(trait_name: &str, method_name: &str, docs: Option<&str> Ok(()) } +fn emit_permissions(out: &mut String, perms: &MethodPermissions) { + writeln!(out, " permissions: {{").unwrap(); + if let Some(auth) = &perms.auth { + writeln!(out, " auth: {},", ts_string_literal(auth)).unwrap(); + } + if let Some(prompt) = &perms.prompt { + writeln!(out, " prompt: {},", ts_string_literal(prompt)).unwrap(); + } + if let Some(ptype) = &perms.permission_type { + writeln!(out, " permissionType: {},", ts_string_literal(ptype)).unwrap(); + } + if let Some(denial) = &perms.denial_error { + writeln!(out, " denialError: {},", ts_string_literal(denial)).unwrap(); + } + writeln!(out, " }},").unwrap(); +} + pub(super) fn playground_type_name(value: &str) -> String { value.replace("T.", "") } diff --git a/rust/crates/truapi/src/api/account.rs b/rust/crates/truapi/src/api/account.rs index 7c4e065f..8ff7a30d 100644 --- a/rust/crates/truapi/src/api/account.rs +++ b/rust/crates/truapi/src/api/account.rs @@ -34,6 +34,10 @@ pub trait Account: Send + Sync { /// Retrieve a product-scoped account. /// + /// # Permissions + /// + /// - **auth**: required + /// /// ```ts /// const result = await truapi.account.getAccount({ /// productAccountId: { @@ -55,6 +59,10 @@ pub trait Account: Send + Sync { /// Retrieve a contextual alias for a product account. /// + /// # Permissions + /// + /// - **auth**: required + /// /// ```ts /// const result = await truapi.account.getAccountAlias({ /// productAccountId: { @@ -76,6 +84,10 @@ pub trait Account: Send + Sync { /// Generate a ring VRF proof for a product account. /// + /// # Permissions + /// + /// - **auth**: required + /// /// ```ts /// import { PASEO_NEXT_V2_ASSET_HUB } from "@parity/truapi"; /// @@ -105,6 +117,10 @@ pub trait Account: Send + Sync { /// List non-product accounts the user owns. /// + /// # Permissions + /// + /// - **auth**: required + /// /// ```ts /// const result = await truapi.account.getLegacyAccounts(); /// assert(result.isOk(), "getLegacyAccounts failed:", result); @@ -121,6 +137,12 @@ pub trait Account: Send + Sync { /// Fetch the user's primary identity. /// + /// # Permissions + /// + /// - **auth**: required + /// - **prompt**: identity disclosure confirmation + /// - **denial_error**: PermissionDenied + /// /// ```ts /// const result = await truapi.account.getUserId(); /// assert(result.isOk(), "getUserId failed:", result); diff --git a/rust/crates/truapi/src/api/chain.rs b/rust/crates/truapi/src/api/chain.rs index 158dd12f..8654ac0f 100644 --- a/rust/crates/truapi/src/api/chain.rs +++ b/rust/crates/truapi/src/api/chain.rs @@ -335,6 +335,10 @@ pub trait Chain: Send + Sync { /// Broadcast a signed transaction. /// + /// # Permissions + /// + /// - **permission**: RemotePermission::ChainSubmit + /// /// ```ts /// import { PASEO_NEXT_V2_ASSET_HUB } from "@parity/truapi"; /// @@ -359,6 +363,10 @@ pub trait Chain: Send + Sync { /// Stop a transaction broadcast. /// + /// # Permissions + /// + /// - **permission**: RemotePermission::ChainSubmit + /// /// ```ts /// import { PASEO_NEXT_V2_ASSET_HUB } from "@parity/truapi"; /// diff --git a/rust/crates/truapi/src/api/chat.rs b/rust/crates/truapi/src/api/chat.rs index 823d0f68..edcb2309 100644 --- a/rust/crates/truapi/src/api/chat.rs +++ b/rust/crates/truapi/src/api/chat.rs @@ -14,6 +14,11 @@ use crate::{CallContext, CallError, Subscription}; pub trait Chat: Send + Sync { /// Create a chat room. /// + /// # Permissions + /// + /// - **auth**: required + /// - **prompt**: chat room creation confirmation + /// /// ```ts /// const result = await truapi.chat.createRoom({ /// roomId: "test-room", @@ -34,6 +39,11 @@ pub trait Chat: Send + Sync { /// Register a chat bot. /// + /// # Permissions + /// + /// - **auth**: required + /// - **prompt**: bot registration confirmation + /// /// ```ts /// const result = await truapi.chat.registerBot({ /// botId: "test-bot", @@ -54,6 +64,10 @@ pub trait Chat: Send + Sync { /// Subscribe to the list of chat rooms. /// + /// # Permissions + /// + /// - **auth**: required + /// /// ```ts /// import { firstValueFrom, from } from "rxjs"; /// @@ -69,6 +83,10 @@ pub trait Chat: Send + Sync { /// Post a message to a chat room. /// + /// # Permissions + /// + /// - **auth**: required + /// /// ```ts /// const result = await truapi.chat.postMessage({ /// roomId: "test-room", @@ -88,6 +106,10 @@ pub trait Chat: Send + Sync { /// Subscribe to received chat actions. /// + /// # Permissions + /// + /// - **auth**: required + /// /// ```ts /// import { firstValueFrom, from } from "rxjs"; /// @@ -108,6 +130,10 @@ pub trait Chat: Send + Sync { /// emitted item is a [`CustomRendererNode`](crate::v01::CustomRendererNode) /// tree describing the rendered UI. /// + /// # Permissions + /// + /// - **auth**: required + /// /// ```ts /// import { firstValueFrom, from } from "rxjs"; /// diff --git a/rust/crates/truapi/src/api/entropy.rs b/rust/crates/truapi/src/api/entropy.rs index 2e52f212..2fad9908 100644 --- a/rust/crates/truapi/src/api/entropy.rs +++ b/rust/crates/truapi/src/api/entropy.rs @@ -10,6 +10,10 @@ use crate::{CallContext, CallError}; pub trait Entropy: Send + Sync { /// Derive deterministic entropy. /// + /// # Permissions + /// + /// - **auth**: required + /// /// ```ts /// const result = await truapi.entropy.derive({ /// context: "0x70726f647563742d6b6579", diff --git a/rust/crates/truapi/src/api/notifications.rs b/rust/crates/truapi/src/api/notifications.rs index 69287bcd..95a7da69 100644 --- a/rust/crates/truapi/src/api/notifications.rs +++ b/rust/crates/truapi/src/api/notifications.rs @@ -20,6 +20,10 @@ pub trait Notifications: Send + Sync { /// /// [RFC 0019]: https://github.com/paritytech/truapi/blob/main/docs/rfcs/0019-scheduled-notifications.md /// + /// # Permissions + /// + /// - **permission**: DevicePermission::Notifications + /// /// ```ts /// const result = await truapi.notifications.sendPushNotification({ /// text: "Hello!", @@ -41,6 +45,10 @@ pub trait Notifications: Send + Sync { /// /// [RFC 0019]: https://github.com/paritytech/truapi/blob/main/docs/rfcs/0019-scheduled-notifications.md /// + /// # Permissions + /// + /// - **permission**: DevicePermission::Notifications + /// /// ```ts /// const result = await truapi.notifications.cancelPushNotification({ /// id: 1, diff --git a/rust/crates/truapi/src/api/payment.rs b/rust/crates/truapi/src/api/payment.rs index cd1d3aae..554d5cba 100644 --- a/rust/crates/truapi/src/api/payment.rs +++ b/rust/crates/truapi/src/api/payment.rs @@ -14,6 +14,12 @@ use crate::{CallContext, CallError, Subscription}; pub trait Payment: Send + Sync { /// Subscribe to payment balance updates. /// + /// # Permissions + /// + /// - **auth**: required + /// - **prompt**: balance disclosure (first call) + /// - **denial_error**: PermissionDenied + /// /// ```ts /// import { firstValueFrom, from } from "rxjs"; /// @@ -36,6 +42,12 @@ pub trait Payment: Send + Sync { /// Request a payment from the user. /// + /// # Permissions + /// + /// - **auth**: required + /// - **prompt**: payment confirmation + /// - **denial_error**: Rejected + /// /// ```ts /// // Fund the balance first so the request is not rejected for lack of funds. /// const topUp = await truapi.payment.topUp({ @@ -63,6 +75,10 @@ pub trait Payment: Send + Sync { /// Subscribe to payment lifecycle updates for a specific payment. /// + /// # Permissions + /// + /// - **auth**: required + /// /// ```ts /// import { firstValueFrom, from } from "rxjs"; /// @@ -103,6 +119,11 @@ pub trait Payment: Send + Sync { /// Top up the user's payment balance. /// + /// # Permissions + /// + /// - **auth**: required + /// - **prompt**: conditional (source-dependent) + /// /// ```ts /// const result = await truapi.payment.topUp({ /// amount: 1000n, diff --git a/rust/crates/truapi/src/api/permissions.rs b/rust/crates/truapi/src/api/permissions.rs index 60fc685f..03c7252c 100644 --- a/rust/crates/truapi/src/api/permissions.rs +++ b/rust/crates/truapi/src/api/permissions.rs @@ -11,6 +11,10 @@ use crate::{CallContext, CallError}; pub trait Permissions: Send + Sync { /// Request a device-capability permission from the user. /// + /// # Permissions + /// + /// - **prompt**: device permission grant dialog + /// /// ```ts /// const result = await truapi.permissions.requestDevicePermission("Camera"); /// assert(result.isOk(), "requestDevicePermission failed:", result); @@ -25,6 +29,10 @@ pub trait Permissions: Send + Sync { /// Request a remote-operation permission. /// + /// # Permissions + /// + /// - **prompt**: remote permission grant dialog + /// /// ```ts /// const result = await truapi.permissions.requestRemotePermission({ /// permission: { tag: "Remote", value: { domains: ["api.example.com"] } }, diff --git a/rust/crates/truapi/src/api/preimage.rs b/rust/crates/truapi/src/api/preimage.rs index 455d54df..fc9d2d64 100644 --- a/rust/crates/truapi/src/api/preimage.rs +++ b/rust/crates/truapi/src/api/preimage.rs @@ -34,6 +34,10 @@ pub trait Preimage: Send + Sync { /// Submit a preimage. Returns the preimage key (hash) on success. /// + /// # Permissions + /// + /// - **permission**: RemotePermission::PreimageSubmit + /// /// ```ts /// const result = await truapi.preimage.submit("0xdeadbeef"); /// assert(result.isOk(), "submit failed:", result); diff --git a/rust/crates/truapi/src/api/resource_allocation.rs b/rust/crates/truapi/src/api/resource_allocation.rs index eb5ea3c1..bd5b8a96 100644 --- a/rust/crates/truapi/src/api/resource_allocation.rs +++ b/rust/crates/truapi/src/api/resource_allocation.rs @@ -11,6 +11,11 @@ use crate::{CallContext, CallError}; pub trait ResourceAllocation: Send + Sync { /// Request the host to pre-allocate one or more resources. /// + /// # Permissions + /// + /// - **auth**: required + /// - **prompt**: per-resource allocation confirmation + /// /// ```ts /// const result = await truapi.resourceAllocation.request({ /// resources: [ diff --git a/rust/crates/truapi/src/api/signing.rs b/rust/crates/truapi/src/api/signing.rs index 6bc4db1e..d170adb5 100644 --- a/rust/crates/truapi/src/api/signing.rs +++ b/rust/crates/truapi/src/api/signing.rs @@ -19,6 +19,12 @@ use crate::{CallContext, CallError}; pub trait Signing: Send + Sync { /// Construct a signed transaction for a product account. /// + /// # Permissions + /// + /// - **auth**: required + /// - **prompt**: signing confirmation + /// - **denial_error**: Rejected + /// /// ```ts /// import { PASEO_NEXT_V2_ASSET_HUB } from "@parity/truapi"; /// @@ -46,6 +52,12 @@ pub trait Signing: Send + Sync { /// Construct a signed transaction for a non-product (legacy) account. /// + /// # Permissions + /// + /// - **auth**: required + /// - **prompt**: signing confirmation + /// - **denial_error**: Rejected + /// /// ```ts /// import { PASEO_NEXT_V2_ASSET_HUB } from "@parity/truapi"; /// @@ -77,6 +89,12 @@ pub trait Signing: Send + Sync { /// Sign raw bytes with a non-product account. /// + /// # Permissions + /// + /// - **auth**: required + /// - **prompt**: signing confirmation + /// - **denial_error**: Rejected + /// /// ```ts /// const result = await truapi.signing.signRawWithLegacyAccount({ /// signer: "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", @@ -100,6 +118,12 @@ pub trait Signing: Send + Sync { /// Sign an extrinsic payload with a non-product account. /// + /// # Permissions + /// + /// - **auth**: required + /// - **prompt**: signing confirmation + /// - **denial_error**: Rejected + /// /// ```ts /// import { PASEO_NEXT_V2_ASSET_HUB } from "@parity/truapi"; /// @@ -136,6 +160,12 @@ pub trait Signing: Send + Sync { /// Sign raw bytes or a message. /// + /// # Permissions + /// + /// - **auth**: required + /// - **prompt**: signing confirmation + /// - **denial_error**: Rejected + /// /// ```ts /// const result = await truapi.signing.signRaw({ /// account: { dotNsIdentifier: "truapi-playground.dot", derivationIndex: 0 }, @@ -160,6 +190,12 @@ pub trait Signing: Send + Sync { /// Sign an extrinsic payload. /// + /// # Permissions + /// + /// - **auth**: required + /// - **prompt**: signing confirmation + /// - **denial_error**: Rejected + /// /// ```ts /// import { PASEO_NEXT_V2_ASSET_HUB } from "@parity/truapi"; /// diff --git a/rust/crates/truapi/src/api/statement_store.rs b/rust/crates/truapi/src/api/statement_store.rs index 5addc9b7..762e8c44 100644 --- a/rust/crates/truapi/src/api/statement_store.rs +++ b/rust/crates/truapi/src/api/statement_store.rs @@ -66,6 +66,12 @@ pub trait StatementStore: Send + Sync { /// instead, which uses a pre-allocated allowance account and does not /// require a per-call signing prompt. /// + /// # Permissions + /// + /// - **auth**: required + /// - **prompt**: signing confirmation + /// - **denial_error**: Rejected + /// /// ```ts /// // Expiry packs a Unix-seconds timestamp in the high 32 bits; a day out /// // keeps the statement unexpired when it is submitted. @@ -98,6 +104,10 @@ pub trait StatementStore: Send + Sync { /// Create a proof for a statement using a pre-allocated allowance account, /// bypassing the per-call signing prompt. /// + /// # Permissions + /// + /// - **auth**: required + /// /// ```ts /// // Expiry packs a Unix-seconds timestamp in the high 32 bits; a day out /// // keeps the statement unexpired when it is submitted. @@ -126,6 +136,10 @@ pub trait StatementStore: Send + Sync { /// [`SignedStatement`](crate::v01::SignedStatement) directly (no wrapping /// struct), matching upstream `triangle-js-sdks`. /// + /// # Permissions + /// + /// - **permission**: RemotePermission::StatementSubmit + /// /// ```ts /// const bytes = crypto.getRandomValues(new Uint8Array(32)); /// const topic: `0x${string}` = `0x${bytes.toHex()}`; diff --git a/rust/crates/truapi/src/api/system.rs b/rust/crates/truapi/src/api/system.rs index 216cd45f..482de0ac 100644 --- a/rust/crates/truapi/src/api/system.rs +++ b/rust/crates/truapi/src/api/system.rs @@ -57,6 +57,10 @@ pub trait System: Send + Sync { /// Request the host to open a URL. /// + /// # Permissions + /// + /// - **permission**: DevicePermission::OpenUrl + /// /// ```ts /// const result = await truapi.system.navigateTo({ /// url: "https://example.com", From c8b815daf889dc99d820b5d97e5da33aed2a3dad Mon Sep 17 00:00:00 2001 From: Filippo Vecchiato Date: Mon, 22 Jun 2026 11:41:49 +0200 Subject: [PATCH 2/2] style: fix rustfmt formatting --- rust/crates/truapi-codegen/src/ts/playground.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/rust/crates/truapi-codegen/src/ts/playground.rs b/rust/crates/truapi-codegen/src/ts/playground.rs index 54dd3214..15662e5f 100644 --- a/rust/crates/truapi-codegen/src/ts/playground.rs +++ b/rust/crates/truapi-codegen/src/ts/playground.rs @@ -316,7 +316,12 @@ fn emit_permissions(out: &mut String, perms: &MethodPermissions) { writeln!(out, " prompt: {},", ts_string_literal(prompt)).unwrap(); } if let Some(ptype) = &perms.permission_type { - writeln!(out, " permissionType: {},", ts_string_literal(ptype)).unwrap(); + writeln!( + out, + " permissionType: {},", + ts_string_literal(ptype) + ) + .unwrap(); } if let Some(denial) = &perms.denial_error { writeln!(out, " denialError: {},", ts_string_literal(denial)).unwrap();