diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8dc1fa1f7..bee04173a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,9 @@
### Fixes
+* [FIX][all] **Activating a device key on a migrated Guardian account no longer permanently breaks its sync.** After a structural rotation (`update_signers` for replace-hot-key, or the follow-up `update_procedure_threshold`), the wallet now pushes the new on-chain state back to the guardian. The OpenZeppelin multisig client only re-registers post-execution state on the guardian for `switch_guardian`; for the other structural ops it submitted the transaction on-chain but left the guardian serving the pre-rotation blob, so every subsequent ~3s guardian sync saw guardian-commitment ≠ on-chain and threw on `ensureSafeToOverwriteLocalState` — permanently, until a full reinstall. The re-registration runs before the hot-key pointer swap arms the sync, and the guardian sync now self-heals a lagging guardian by re-registering the current state once per run (which also recovers already-broken accounts without a reinstall). The legacy-Guardian migration additionally verifies the re-derived cold key matches the on-chain signer before flagging an account for activation. (#227)
+* [FIX][all] **Guardian sync no longer spams "missing hotPublicKey" errors for un-activated accounts.** `syncGuardianAccounts` now only syncs Guardian accounts that actually carry a hot key, instead of every account not flagged `requiresHotKeyRotation`. A legacy single-signer Guardian record that hasn't been migrated yet — e.g. in the brief window after a wallet upgrade (new code, old storage) and before the forced re-unlock runs the migration — has no hot key, so `getOrCreateMultisigService` threw on it every ~3s AutoSync tick. There's genuinely no hot-bound service to build for such accounts, so they're skipped; recovery still happens via the migration → Activate Device Key banner path on the next unlock. (#227)
+* [FIX][all] **Guardian operator endpoint is now tracked per account, and structural Guardian ops recover from a submit-then-local-apply failure.** Each Guardian account persists its own `guardianEndpoint` (set at create/recovery, updated on switch-guardian) instead of a single global setting, so two Guardian accounts on different operators no longer collide — older records without the field fall back to the legacy global value. Separately, when a `replace-hot-key` or `switch-guardian` transaction lands on chain but the local apply step throws, the wallet now runs the same finalization the happy path would (swapping the hot-key pointer, or re-registering on the new guardian and persisting its endpoint) instead of cancelling — which previously stranded the account signing with a rotated-out key or talking to the old guardian. (#227)
* [FIX][all] **Guardian co-sign transactions are now serialized per account.** The Guardian co-signs one delta per account at a time, so concurrent same-account transactions (e.g. auto-consume racing a user claim, or rapid successive claims) made the Guardian's expected commitment diverge from on-chain — stalling its canonicalization for minutes and returning `409 ConflictPendingDelta` while a prior delta was still finalizing, which surfaced as a Guardian claim/send that never completes. Guardian transactions now take a per-account lock so at most one is ever in flight, and proposal creation waits out a transient `409` (a prior delta mid-canonicalization) instead of failing the transaction. (#297)
## 1.15.2 (2026-06-22)
@@ -33,6 +36,8 @@
### Features
+* [FEATURE][all] 3-key Guardian: cold co-signs `switch_guardian` (on-chain threshold-2 satisfied via `procedureThresholds`) and a new Settings → Rotate Device Key flow lets users replace the hot signer in-place via a single cold-signed `update_signers` proposal, persisting the new ciphertext before submission and finalizing `WalletAccount.hotPublicKey` (plus releasing the old SE/StrongBox wrapper) only after on-chain inclusion. Settings → Switch Guardian now requires an explicit confirmation step before initiating, mirroring the Rotate Device Key flow so the user acknowledges that the switch is cold-signed + guardian-co-signed. Guardian import-by-seed does lookup + adopt only (`MultisigClient.recoverByKey` per HD index until first miss); each adopted account is flagged `requiresHotKeyRotation` and the home view surfaces an Activate Device Key banner with a one-click CTA that fires the same cold-signed `update_signers` rotation — the banner self-hides once `Vault.swapHotKey` lands. The `swapHotKey` message now carries only `newHotPubKey`; the vault resolves the previous hot from the persisted `WalletAccount`, so the initial post-recovery activation and subsequent rotations share one code path.
+* [FEATURE][all] 3-key Guardian accounts. Guardian creation now provisions a hot ECDSA key (random, lives outside the WASM keystore behind a new `secure-hot-key` facade — extension/desktop use a JS fallback; iOS wraps the secret under a per-account Secure Enclave P-256 key via `HotKeyPlugin.swift`; Android wraps it under a per-account StrongBox-backed RSA-OAEP key via `HotKeyPlugin.kt` with biometric-gated unwrap, identical wire format on both platforms) plus an HD-derived cold ECDSA key (kept in the SDK keystore for cold-routed flows) alongside the existing guardian co-signer. Threshold stays at 1 (hot OR cold + guardian); cold-only routing for `update_signers` / `update_guardian` / `update_procedure_threshold` is enforced client-side per the Phase 0 SDK reading. Storage adds `accColdSecretKeyStrgKey` and `WalletAccount.{hotPublicKey,coldPublicKey}` so role-aware `signWord` (Phase 3) can dispatch hot vs cold by storage entity. Hard cutover from the 1-of-1 Falcon scheme — pre-cutover Guardian accounts are unreachable from this build.
* [FEATURE][all] Guardian integration. Adds Guardian-backed accounts (1-of-1 multisig with on-chain Guardian signature verification), onboarding create/import flows with a dedicated recovery-method screen that accepts a custom guardian URL, Settings → Guardian Settings with an on-chain switch-guardian proposal that re-registers post-switch state with the new endpoint, service-worker routing for Guardian transaction signing via `MultisigService`, frontend Guardian sync outside the WASM lock, and per-stage progress labels (`creating-proposal`, `signing-proposal`, `submitting`, `registering-guardian`) in the transaction modal.
* [FEATURE][all] **Default auth scheme for new accounts switched from Falcon to ECDSA.** Existing accounts are unaffected — Miden seals the auth component at on-chain creation and can never rotate it, so any account previously created stays Falcon and continues signing with its existing keystore secret. Restore paths handle both schemes: mnemonic-only restore (`Vault.spawn`) probes the chain under each scheme to find the user's actual hdIndex=0 account; encrypted-file restore reads the new optional `authScheme` field on `WalletAccount` (legacy entries with no field default to Falcon, matching the historical default 1:1). Private-key import detects the scheme from the deserialized `AuthSecretKey` via the SDK's per-scheme accessor. New accounts created post-upgrade get ECDSA stamped into their `WalletAccount` record. Encrypted-file format change is purely additive — old files round-trip through restore as Falcon. (#229)
diff --git a/CLAUDE.md b/CLAUDE.md
index 9786ab0e6..b6c360065 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -122,6 +122,12 @@ xcrun simctl spawn booted notifyutil -p com.apple.BiometricKit_Sim.fingerTouch.m
- Grey bar at bottom → `100dvh` doesn't account for safe areas. Use `100%` + `env(safe-area-inset-*)` padding on `mobile.html` body.
- Debug UI text should be `select-text` so errors are copyable.
+### Adding Swift files to the App target
+The App target in `ios/App/App.xcodeproj/project.pbxproj` does NOT auto-discover Swift files in the `App/` source directory. New files must be registered in four sections: `PBXBuildFile`, `PBXFileReference`, the App `PBXGroup` children, and the `PBXSourcesBuildPhase` files list. Pattern: see `LocalBiometricPlugin.swift` or `HotKeyPlugin.swift` entries.
+
+### Adding a custom Capacitor plugin (iOS)
+Capacitor on this app uses **manual** registration — not the `CAPBridgedPlugin` auto-discovery you'd get on a stock Capacitor app. After creating `MyPlugin.swift` and wiring it into the four pbxproj sections above, you also have to call `bridge?.registerPluginInstance(MyPlugin())` inside `capacitorDidLoad()` in `ios/App/App/AppViewController.swift`. Skip this step and JS calls land as `{"code":"UNIMPLEMENTED"}` even though the class compiled fine.
+
### Native navbar overlay
Mobile hides React footer and renders bottom nav as native pill (iOS: `MidenNavbarOverlayWindow` `UIWindow`; Android: two-instance `NavbarOverlayManager` with Activity-scoped + Dialog-scoped `NavbarView`). Plugin methods: `showNativeNavbar`, `setNavbarSecondaryRow`, `setNavbarAction`, `morphNavbar{Out,In}`. Events: `nativeNavbarTap`, `nativeNavbarSecondaryTap`, `nativeNavbarActionTap`. Wiring: `src/app/providers/DappBrowserProvider.tsx`. Android gotchas: don't use `MATCH_PARENT` children in `WRAP_CONTENT` parents (1878px buttons); `Dialog.setLayout` must follow `setContentView`; shadow must be on the view owning the background drawable.
diff --git a/__mocks__/@openzeppelin/miden-multisig-client.ts b/__mocks__/@openzeppelin/miden-multisig-client.ts
index 5fccf4855..f13d9f0f5 100644
--- a/__mocks__/@openzeppelin/miden-multisig-client.ts
+++ b/__mocks__/@openzeppelin/miden-multisig-client.ts
@@ -59,6 +59,16 @@ export const buildMultisigStorageSlots = jest.fn();
export const buildGuardianStorageSlots = jest.fn();
export const storageLayoutBuilder = jest.fn();
+// Update-signers + summary builders used by createReplaceHotKeyProposal. The
+// real implementations touch WASM; tests mock-or-spy as needed.
+export const buildUpdateSignersTransactionRequest = jest.fn(async () => ({
+ request: { kind: 'update-signers-request' },
+ salt: { toHex: () => 'salt-hex' }
+}));
+export const executeForSummary = jest.fn(async () => ({
+ serialize: () => new Uint8Array([0xab])
+}));
+
export class StorageLayoutBuilder {
constructor(..._args: unknown[]) {}
}
diff --git a/__mocks__/lib/miden/back/vault.ts b/__mocks__/lib/miden/back/vault.ts
index f00fe9888..ee4cc118d 100644
--- a/__mocks__/lib/miden/back/vault.ts
+++ b/__mocks__/lib/miden/back/vault.ts
@@ -43,6 +43,10 @@ export class Vault {
return state.accounts;
}
+ // Best-effort on-unlock migration of legacy single-key Guardian accounts to
+ // the 3-key model — a no-op in the mock (no real accounts/keys to migrate).
+ async migrateLegacyGuardianAccounts() {}
+
async fetchSettings() {
return state.settings;
}
diff --git a/android/.idea/deploymentTargetSelector.xml b/android/.idea/deploymentTargetSelector.xml
index b268ef36c..ca16a995c 100644
--- a/android/.idea/deploymentTargetSelector.xml
+++ b/android/.idea/deploymentTargetSelector.xml
@@ -4,6 +4,7 @@
+
diff --git a/android/app/build.gradle b/android/app/build.gradle
index 13bfcd46c..ca63c97b4 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -65,6 +65,10 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
// Biometric authentication for hardware security
implementation 'androidx.biometric:biometric:1.1.0'
+ // secp256k1 ECDSA + Keccak-256 for the Guardian hot-key signing path
+ // (HotKeyPlugin). Keystore wraps the 32-byte k256 secret with RSA-OAEP;
+ // BouncyCastle handles everything outside the Keystore boundary.
+ implementation 'org.bouncycastle:bcprov-jdk18on:1.78.1'
}
apply from: 'capacitor.build.gradle'
diff --git a/android/app/proguard-rules.pro b/android/app/proguard-rules.pro
index 4c549e886..b2835525e 100644
--- a/android/app/proguard-rules.pro
+++ b/android/app/proguard-rules.pro
@@ -70,6 +70,12 @@
-keep class com.niceforyou.capacitor.barcodescanner.** { *; }
-keep class com.google.mlkit.vision.barcode.** { *; }
+# BouncyCastle (secp256k1 + Keccak-256) — used by HotKeyPlugin for the
+# Guardian hot-key signing path. Keep the low-level crypto + asn1 classes
+# we touch reflectively via JCE provider lookup paths.
+-keep class org.bouncycastle.** { *; }
+-keep class com.miden.wallet.HotKeyPlugin { *; }
+
# Don't warn about missing classes in optional dependencies
-dontwarn org.conscrypt.**
-dontwarn org.bouncycastle.**
diff --git a/android/app/src/main/java/com/miden/wallet/HotKeyPlugin.kt b/android/app/src/main/java/com/miden/wallet/HotKeyPlugin.kt
new file mode 100644
index 000000000..627c6f1c4
--- /dev/null
+++ b/android/app/src/main/java/com/miden/wallet/HotKeyPlugin.kt
@@ -0,0 +1,578 @@
+package com.miden.wallet
+
+import android.os.Build
+import android.security.keystore.KeyGenParameterSpec
+import android.security.keystore.KeyProperties
+import android.security.keystore.StrongBoxUnavailableException
+import android.util.Base64
+import android.util.Log
+import androidx.biometric.BiometricManager
+import androidx.biometric.BiometricPrompt
+import androidx.core.content.ContextCompat
+import androidx.fragment.app.FragmentActivity
+import com.getcapacitor.JSObject
+import com.getcapacitor.Plugin
+import com.getcapacitor.PluginCall
+import com.getcapacitor.PluginMethod
+import com.getcapacitor.annotation.CapacitorPlugin
+import org.bouncycastle.asn1.sec.SECNamedCurves
+import org.bouncycastle.crypto.digests.SHA256Digest
+import org.bouncycastle.crypto.params.ECDomainParameters
+import org.bouncycastle.crypto.params.ECPrivateKeyParameters
+import org.bouncycastle.crypto.signers.ECDSASigner
+import org.bouncycastle.crypto.signers.HMacDSAKCalculator
+import org.bouncycastle.jcajce.provider.digest.Keccak
+import org.bouncycastle.math.ec.ECAlgorithms
+import org.bouncycastle.math.ec.ECPoint
+import java.math.BigInteger
+import java.security.KeyPairGenerator
+import java.security.KeyStore
+import java.security.PrivateKey
+import java.security.PublicKey
+import java.security.SecureRandom
+import java.security.spec.MGF1ParameterSpec
+import javax.crypto.Cipher
+import javax.crypto.spec.OAEPParameterSpec
+import javax.crypto.spec.PSource
+
+// Per-account Guardian "hot" signing key (3-key migration, Phase 4b — Android
+// counterpart of ios/App/App/HotKeyPlugin.swift).
+//
+// Storage layout mirrors iOS so the JS facade stays platform-agnostic:
+// - Per-account RSA-2048 key in Android Keystore aliased
+// "com.miden.wallet.hot.", StrongBox-backed when available,
+// auth-bound on the private key only (encrypt with the public key needs
+// no auth, decrypt does — same shape as the iOS SE ECIES path).
+// - Returned ciphertext is ":" so signWith /
+// deleteWith can recover the alias from the blob alone.
+//
+// The k256 secret is wrapped with RSA-OAEP-SHA-256 because Android Keystore
+// can't do EC-encrypt directly; iOS uses ECIES (the SE-native primitive). The
+// wire format (`:`) and signature format (`0x`,
+// 65 bytes hex) are identical across both platforms.
+@CapacitorPlugin(name = "HotKey")
+class HotKeyPlugin : Plugin() {
+
+ companion object {
+ private const val TAG = "HotKey"
+ private const val ANDROID_KEYSTORE = "AndroidKeyStore"
+ private const val KEY_ALIAS_PREFIX = "com.miden.wallet.hot."
+ private const val OAEP_TRANSFORMATION = "RSA/ECB/OAEPWithSHA-256AndMGF1Padding"
+
+ // BC's secp256k1 domain parameters; reused by every sign call.
+ private val SECP256K1 = SECNamedCurves.getByName("secp256k1")
+ private val DOMAIN = ECDomainParameters(SECP256K1.curve, SECP256K1.g, SECP256K1.n, SECP256K1.h)
+ private val HALF_N: BigInteger = SECP256K1.n.shiftRight(1)
+ }
+
+ private enum class PendingOp { SIGN, REVEAL }
+
+ private var pendingCall: PluginCall? = null
+ private var pendingPayload: ByteArray? = null
+ private var pendingDigest: ByteArray? = null
+ private var pendingOp: PendingOp? = null
+
+ @PluginMethod
+ fun generateHotKey(call: PluginCall) {
+ Log.d(TAG, "generateHotKey called")
+
+ val secretBytes = ByteArray(32)
+ var alias: String? = null
+ try {
+ // 1. Random k256 secret + compressed (33-byte: 0x02/0x03 || x) public key.
+ // iOS returns the same 33-byte form; commitmentFromPublicKeyHex on the
+ // JS side rejects anything else.
+ SecureRandom().nextBytes(secretBytes)
+ val publicKeyHex = derivePublicKeyHex(secretBytes)
+
+ // 2. 16-byte tag suffix → base64; full Keystore alias = prefix + suffix.
+ val tagSuffix = ByteArray(16)
+ SecureRandom().nextBytes(tagSuffix)
+ val tagSuffixB64 = Base64.encodeToString(tagSuffix, Base64.NO_WRAP)
+ alias = KEY_ALIAS_PREFIX + tagSuffixB64
+
+ // 3. Generate the per-account RSA wrapper key (StrongBox-preferred,
+ // auth-bound private). Public-key encrypt below needs no auth.
+ val wrapperPub = generateKeystoreWrapperKey(alias)
+
+ // 4. RSA-OAEP-SHA-256 wrap the k256 secret. Same role as the iOS
+ // eciesEncryptionStandardX963SHA256AESGCM blob — opaque to JS.
+ val cipher = Cipher.getInstance(OAEP_TRANSFORMATION)
+ cipher.init(Cipher.ENCRYPT_MODE, wrapperPub, oaepParams())
+ val wrapped = cipher.doFinal(secretBytes)
+
+ // 5. Pack into ":" so signWithHotKey can
+ // recover the alias from the ciphertext alone.
+ val packed = tagSuffixB64 + ":" + Base64.encodeToString(wrapped, Base64.NO_WRAP)
+
+ val result = JSObject()
+ result.put("ciphertext", packed)
+ result.put("publicKeyHex", publicKeyHex)
+ Log.d(TAG, "generateHotKey success")
+ call.resolve(result)
+ } catch (e: Exception) {
+ Log.e(TAG, "generateHotKey failed: ${e.message}", e)
+ // Best-effort cleanup of the orphan Keystore key we may have just created.
+ alias?.let { deleteAliasQuietly(it) }
+ call.reject("Failed to generate hot key: ${e.message}")
+ } finally {
+ zero(secretBytes)
+ }
+ }
+
+ /// Unwrap the hot-key secret inside the Keystore (triggers BiometricPrompt),
+ /// Keccak-256 the supplied 32-byte word, ECDSA-sign (recoverable) over
+ /// secp256k1, and return r||s||v as 0x-prefixed hex (65 bytes). The
+ /// unwrapped secret is zeroed before returning.
+ @PluginMethod
+ fun signWithHotKey(call: PluginCall) {
+ Log.d(TAG, "signWithHotKey called")
+
+ // The BiometricPrompt callback reads instance fields (pendingCall/
+ // pendingPayload/pendingDigest); a second concurrent call would clobber
+ // them, crossing signatures and responses. Reject while one is in flight.
+ if (pendingCall != null) {
+ call.reject("Another hot-key operation is in progress", "BIOMETRIC_BUSY")
+ return
+ }
+
+ val ciphertext = call.getString("ciphertext")
+ val digestHex = call.getString("digestHex")
+ if (ciphertext == null || digestHex == null) {
+ call.reject("Missing 'ciphertext' or 'digestHex' parameter")
+ return
+ }
+
+ try {
+ // 1. Split tag from payload.
+ val (alias, payload) = parseCiphertext(ciphertext)
+
+ // 2. Decode the digest (caller passes it 0x-prefixed, matching
+ // Word.toHex()). Must be 32 bytes — Miden Words are 4 felts × 8.
+ val cleaned = if (digestHex.startsWith("0x")) digestHex.substring(2) else digestHex
+ val digestBytes = hexDecode(cleaned)
+ if (digestBytes.size != 32) {
+ call.reject("Hot-key digest must be 32 hex bytes")
+ return
+ }
+
+ // 3. Look up the Keystore wrapper private key by alias.
+ val keyStore = KeyStore.getInstance(ANDROID_KEYSTORE)
+ keyStore.load(null)
+ val privateKey = keyStore.getKey(alias, null) as? PrivateKey
+ if (privateKey == null) {
+ Log.e(TAG, "signWithHotKey: Keystore key not found at $alias")
+ call.reject("Hot-key Keystore key not found", "KEY_NOT_FOUND")
+ return
+ }
+
+ // 4. Initialize the OAEP cipher in DECRYPT_MODE; the actual doFinal
+ // runs inside the BiometricPrompt callback, which is what gates
+ // the unwrap on user presence (mirrors SecKeyCreateDecryptedData
+ // on iOS).
+ val cipher = Cipher.getInstance(OAEP_TRANSFORMATION)
+ cipher.init(Cipher.DECRYPT_MODE, privateKey, oaepParams())
+
+ pendingCall = call
+ pendingPayload = payload
+ pendingDigest = digestBytes
+ pendingOp = PendingOp.SIGN
+ promptForBiometric(cipher, "Sign transaction")
+ } catch (e: Exception) {
+ Log.e(TAG, "signWithHotKey failed: ${e.message}", e)
+ call.reject("Hot-key sign failed: ${e.message}")
+ }
+ }
+
+ /// Unwrap the hot-key secret inside the Keystore (triggers BiometricPrompt)
+ /// and return the raw 32-byte secp256k1 secret as hex. Used by Settings →
+ /// Reveal Hot Key. Same OAEP unwrap path as `signWithHotKey`, minus the
+ /// signing step. The unwrapped secret is zeroed before returning.
+ @PluginMethod
+ fun revealHotKey(call: PluginCall) {
+ Log.d(TAG, "revealHotKey called")
+
+ // See signWithHotKey: reject while a biometric op is already in flight so
+ // the shared pending* fields aren't clobbered.
+ if (pendingCall != null) {
+ call.reject("Another hot-key operation is in progress", "BIOMETRIC_BUSY")
+ return
+ }
+
+ val ciphertext = call.getString("ciphertext")
+ if (ciphertext == null) {
+ call.reject("Missing 'ciphertext' parameter")
+ return
+ }
+
+ try {
+ val (alias, payload) = parseCiphertext(ciphertext)
+
+ val keyStore = KeyStore.getInstance(ANDROID_KEYSTORE)
+ keyStore.load(null)
+ val privateKey = keyStore.getKey(alias, null) as? PrivateKey
+ if (privateKey == null) {
+ Log.e(TAG, "revealHotKey: Keystore key not found at $alias")
+ call.reject("Hot-key Keystore key not found", "KEY_NOT_FOUND")
+ return
+ }
+
+ val cipher = Cipher.getInstance(OAEP_TRANSFORMATION)
+ cipher.init(Cipher.DECRYPT_MODE, privateKey, oaepParams())
+
+ pendingCall = call
+ pendingPayload = payload
+ pendingDigest = null
+ pendingOp = PendingOp.REVEAL
+ promptForBiometric(cipher, "Reveal device key")
+ } catch (e: Exception) {
+ Log.e(TAG, "revealHotKey failed: ${e.message}", e)
+ call.reject("Hot-key reveal failed: ${e.message}")
+ }
+ }
+
+ /// Delete the per-account Keystore hot key. Idempotent — a missing alias
+ /// resolves successfully so callers can call this during account deletion
+ /// without branching on existence.
+ @PluginMethod
+ fun deleteHotKey(call: PluginCall) {
+ Log.d(TAG, "deleteHotKey called")
+
+ val ciphertext = call.getString("ciphertext")
+ if (ciphertext == null) {
+ call.reject("Missing 'ciphertext' parameter")
+ return
+ }
+
+ try {
+ val parts = ciphertext.split(":", limit = 2)
+ if (parts.isEmpty() || parts[0].isEmpty()) {
+ call.reject("Malformed hot-key ciphertext")
+ return
+ }
+ val alias = KEY_ALIAS_PREFIX + parts[0]
+ deleteAliasQuietly(alias)
+ Log.d(TAG, "deleteHotKey resolved")
+ call.resolve()
+ } catch (e: Exception) {
+ Log.e(TAG, "deleteHotKey failed: ${e.message}", e)
+ call.reject("Failed to delete hot key: ${e.message}")
+ }
+ }
+
+ // -- Biometric prompt + post-auth sign ------------------------------------
+
+ private fun promptForBiometric(cipher: Cipher, subtitle: String) {
+ val activity = activity as? FragmentActivity
+ if (activity == null) {
+ failPending("Activity not available")
+ return
+ }
+
+ val executor = ContextCompat.getMainExecutor(context)
+ val callback = object : BiometricPrompt.AuthenticationCallback() {
+ override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
+ Log.d(TAG, "Biometric authentication succeeded for hot-key op")
+ val cryptoCipher = result.cryptoObject?.cipher
+ val payload = pendingPayload
+ val digest = pendingDigest
+ val op = pendingOp
+ val pendingCallLocal = pendingCall
+ pendingCall = null
+ pendingPayload = null
+ pendingDigest = null
+ pendingOp = null
+
+ if (cryptoCipher == null || payload == null || op == null || pendingCallLocal == null) {
+ pendingCallLocal?.reject("Cipher not available after authentication")
+ return
+ }
+
+ var unwrapped: ByteArray? = null
+ try {
+ unwrapped = cryptoCipher.doFinal(payload)
+ if (unwrapped.size != 32) {
+ pendingCallLocal.reject("Unwrapped hot-key has wrong length")
+ return
+ }
+ when (op) {
+ PendingOp.SIGN -> {
+ if (digest == null) {
+ pendingCallLocal.reject("Hot-key sign post-auth: missing digest")
+ return
+ }
+ val signatureHex = signRecoverable(unwrapped, digest)
+ val res = JSObject()
+ res.put("signatureHex", signatureHex)
+ Log.d(TAG, "signWithHotKey success")
+ pendingCallLocal.resolve(res)
+ }
+ PendingOp.REVEAL -> {
+ val secretKeyHex = unwrapped.toHex()
+ val res = JSObject()
+ res.put("secretKeyHex", secretKeyHex)
+ Log.d(TAG, "revealHotKey success")
+ pendingCallLocal.resolve(res)
+ }
+ }
+ } catch (e: Exception) {
+ Log.e(TAG, "Hot-key post-auth failed: ${e.message}", e)
+ pendingCallLocal.reject("Hot-key operation failed: ${e.message}")
+ } finally {
+ unwrapped?.let { zero(it) }
+ }
+ }
+
+ override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
+ Log.e(TAG, "Biometric authentication error: $errorCode - $errString")
+ val pendingCallLocal = pendingCall
+ pendingCall = null
+ pendingPayload = null
+ pendingDigest = null
+ pendingOp = null
+ when (errorCode) {
+ BiometricPrompt.ERROR_USER_CANCELED,
+ BiometricPrompt.ERROR_NEGATIVE_BUTTON ->
+ pendingCallLocal?.reject("Authentication cancelled", "USER_CANCELLED")
+ else ->
+ pendingCallLocal?.reject("Authentication failed: $errString", "AUTH_FAILED")
+ }
+ }
+
+ override fun onAuthenticationFailed() {
+ Log.d(TAG, "Biometric authentication failed (user can retry)")
+ }
+ }
+
+ // Match HardwareSecurityPlugin: biometric-strong OR device credential,
+ // no negative button (DEVICE_CREDENTIAL forbids it).
+ val promptInfo = BiometricPrompt.PromptInfo.Builder()
+ .setTitle("Miden Wallet")
+ .setSubtitle(subtitle)
+ .setAllowedAuthenticators(
+ BiometricManager.Authenticators.BIOMETRIC_STRONG or
+ BiometricManager.Authenticators.DEVICE_CREDENTIAL
+ )
+ .build()
+
+ activity.runOnUiThread {
+ val biometricPrompt = BiometricPrompt(activity, executor, callback)
+ biometricPrompt.authenticate(promptInfo, BiometricPrompt.CryptoObject(cipher))
+ }
+ }
+
+ private fun failPending(msg: String) {
+ val pendingCallLocal = pendingCall
+ pendingCall = null
+ pendingPayload = null
+ pendingDigest = null
+ pendingOp = null
+ pendingCallLocal?.reject(msg)
+ }
+
+ // -- Keystore wrapper key -------------------------------------------------
+
+ private fun generateKeystoreWrapperKey(alias: String): PublicKey {
+ // StrongBox first when the hardware supports it; fall back to TEE on
+ // StrongBoxUnavailableException so older devices still work. Pattern
+ // intentionally tries StrongBox, catches the throw, rebuilds without
+ // the flag — same trade-off the iOS plugin makes when omitting
+ // kSecAttrTokenIDSecureEnclave on simulator.
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+ try {
+ return generateRsaWrapperKey(alias, strongBox = true)
+ } catch (e: StrongBoxUnavailableException) {
+ Log.w(TAG, "StrongBox unavailable, falling back to TEE")
+ deleteAliasQuietly(alias) // partial init can leave a stale entry
+ }
+ }
+ return generateRsaWrapperKey(alias, strongBox = false)
+ }
+
+ private fun generateRsaWrapperKey(alias: String, strongBox: Boolean): PublicKey {
+ val gen = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, ANDROID_KEYSTORE)
+ val builder = KeyGenParameterSpec.Builder(
+ alias,
+ KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
+ )
+ .setKeySize(2048)
+ .setDigests(KeyProperties.DIGEST_SHA256)
+ .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_OAEP)
+ .setUserAuthenticationRequired(true)
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ // Android 11+: biometric-strong OR device credential, every-use auth.
+ builder.setUserAuthenticationParameters(
+ 0,
+ KeyProperties.AUTH_BIOMETRIC_STRONG or KeyProperties.AUTH_DEVICE_CREDENTIAL
+ )
+ } else {
+ // Pre-Android-11: every-use auth, but BIOMETRIC_STRONG cannot be
+ // enforced here — on API 23–29 a non-FIDO-certified (BIOMETRIC_WEAK)
+ // fingerprint sensor can satisfy the guard. Key strength is therefore
+ // hardware-dependent on these older devices (StrongBox below still
+ // applies when present). `-1` = authentication required on every use.
+ @Suppress("DEPRECATION")
+ builder.setUserAuthenticationValidityDurationSeconds(-1)
+ }
+
+ if (strongBox && Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+ builder.setIsStrongBoxBacked(true)
+ }
+
+ gen.initialize(builder.build())
+ return gen.generateKeyPair().public
+ }
+
+ private fun deleteAliasQuietly(alias: String) {
+ try {
+ val keyStore = KeyStore.getInstance(ANDROID_KEYSTORE)
+ keyStore.load(null)
+ if (keyStore.containsAlias(alias)) {
+ keyStore.deleteEntry(alias)
+ }
+ } catch (e: Exception) {
+ Log.w(TAG, "deleteAliasQuietly($alias) ignored: ${e.message}")
+ }
+ }
+
+ // -- secp256k1 + Keccak-256 -----------------------------------------------
+
+ /**
+ * Compressed secp256k1 public key (33 bytes: 0x02/0x03 parity prefix +
+ * 32-byte x). Matches what iOS returns via `P256K.Signing.PrivateKey
+ * .publicKey.dataRepresentation`; the JS facade asserts `length === 33`.
+ */
+ private fun derivePublicKeyHex(secret: ByteArray): String {
+ val d = BigInteger(1, secret)
+ val q = SECP256K1.g.multiply(d).normalize()
+ return q.getEncoded(true).toHex()
+ }
+
+ /**
+ * Keccak-256 the 32-byte word, then ECDSA-sign (recoverable) with
+ * deterministic-k (RFC 6979) over secp256k1. Returns r||s||v as a
+ * 0x-prefixed hex string (65 bytes). Matches the iOS plugin which uses
+ * libsecp256k1's `secp256k1_ecdsa_sign_recoverable`. We canonicalize s
+ * to the low half of n so signatures are uniquely-encoded — the iOS
+ * libsecp256k1 path does the same internally.
+ */
+ private fun signRecoverable(secret: ByteArray, digestBytes: ByteArray): String {
+ val keccak = Keccak.Digest256()
+ val hash = keccak.digest(digestBytes)
+
+ val d = BigInteger(1, secret)
+ val signer = ECDSASigner(HMacDSAKCalculator(SHA256Digest()))
+ signer.init(true, ECPrivateKeyParameters(d, DOMAIN))
+ val rs = signer.generateSignature(hash)
+ val r = rs[0]
+ var s = rs[1]
+ if (s > HALF_N) s = SECP256K1.n.subtract(s)
+
+ val pubKey = SECP256K1.g.multiply(d).normalize()
+ val v = computeRecoveryId(r, s, hash, pubKey)
+
+ val sig = ByteArray(65)
+ System.arraycopy(toFixed32(r), 0, sig, 0, 32)
+ System.arraycopy(toFixed32(s), 0, sig, 32, 32)
+ sig[64] = v
+ return "0x" + sig.toHex()
+ }
+
+ private fun computeRecoveryId(r: BigInteger, s: BigInteger, hash: ByteArray, expected: ECPoint): Byte {
+ // For low-s canonical signatures with x < n there are at most 2
+ // candidates (recId 0 and 1). Try both, pick the one that recovers
+ // back to the signer's actual public key.
+ for (recId in 0..1) {
+ val recovered = recoverPubKey(r, s, hash, recId)
+ if (recovered != null && recovered.equals(expected)) {
+ return recId.toByte()
+ }
+ }
+ throw IllegalStateException("ECDSA recovery id derivation failed")
+ }
+
+ private fun recoverPubKey(r: BigInteger, s: BigInteger, hash: ByteArray, recId: Int): ECPoint? {
+ val n = SECP256K1.n
+ val curve = SECP256K1.curve
+ val prime = curve.field.characteristic
+ val i = BigInteger.valueOf((recId / 2).toLong())
+ val x = r.add(i.multiply(n))
+ if (x >= prime) return null
+ val R = decompressPoint(x, recId and 1 == 1) ?: return null
+ if (!R.multiply(n).isInfinity) return null
+ val e = BigInteger(1, hash)
+ val rInv = r.modInverse(n)
+ val srInv = rInv.multiply(s).mod(n)
+ val eInvrInv = rInv.multiply(n.subtract(e)).mod(n)
+ return ECAlgorithms.sumOfTwoMultiplies(SECP256K1.g, eInvrInv, R, srInv).normalize()
+ }
+
+ private fun decompressPoint(x: BigInteger, yOdd: Boolean): ECPoint? {
+ return try {
+ val enc = ByteArray(33)
+ enc[0] = if (yOdd) 0x03 else 0x02
+ val xb = toFixed32(x)
+ System.arraycopy(xb, 0, enc, 1, 32)
+ SECP256K1.curve.decodePoint(enc)
+ } catch (e: Exception) {
+ null
+ }
+ }
+
+ // -- Helpers ---------------------------------------------------------------
+
+ private fun parseCiphertext(ct: String): Pair {
+ val parts = ct.split(":", limit = 2)
+ if (parts.size != 2 || parts[0].isEmpty()) {
+ throw IllegalArgumentException("Malformed hot-key ciphertext")
+ }
+ val alias = KEY_ALIAS_PREFIX + parts[0]
+ val payload = Base64.decode(parts[1], Base64.NO_WRAP)
+ return alias to payload
+ }
+
+ private fun oaepParams(): OAEPParameterSpec =
+ // Explicit OAEP params: Android Keystore's default MGF1 digest is
+ // SHA-1, which mismatches the SHA-256 main digest we set above.
+ // Spelling it out keeps encrypt and decrypt agreeing.
+ OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, PSource.PSpecified.DEFAULT)
+
+ private fun toFixed32(v: BigInteger): ByteArray {
+ val raw = v.toByteArray()
+ if (raw.size == 32) return raw
+ if (raw.size == 33 && raw[0] == 0.toByte()) {
+ val out = ByteArray(32)
+ System.arraycopy(raw, 1, out, 0, 32)
+ return out
+ }
+ if (raw.size < 32) {
+ val out = ByteArray(32)
+ System.arraycopy(raw, 0, out, 32 - raw.size, raw.size)
+ return out
+ }
+ throw IllegalStateException("BigInteger too large for 32-byte fixed buffer: ${raw.size}")
+ }
+
+ private fun ByteArray.toHex(): String {
+ val sb = StringBuilder(size * 2)
+ for (b in this) sb.append(String.format("%02x", b))
+ return sb.toString()
+ }
+
+ private fun hexDecode(s: String): ByteArray {
+ if (s.length % 2 != 0) throw IllegalArgumentException("invalid hex length")
+ val out = ByteArray(s.length / 2)
+ for (i in out.indices) {
+ val hi = Character.digit(s[i * 2], 16)
+ val lo = Character.digit(s[i * 2 + 1], 16)
+ if (hi < 0 || lo < 0) throw IllegalArgumentException("invalid hex char")
+ out[i] = ((hi shl 4) + lo).toByte()
+ }
+ return out
+ }
+
+ private fun zero(b: ByteArray) {
+ for (i in b.indices) b[i] = 0
+ }
+}
diff --git a/android/app/src/main/java/com/miden/wallet/MainActivity.java b/android/app/src/main/java/com/miden/wallet/MainActivity.java
index 7eff59079..035eca7ec 100644
--- a/android/app/src/main/java/com/miden/wallet/MainActivity.java
+++ b/android/app/src/main/java/com/miden/wallet/MainActivity.java
@@ -17,6 +17,7 @@ public class MainActivity extends BridgeActivity {
protected void onCreate(Bundle savedInstanceState) {
// Register custom plugins before super.onCreate
registerPlugin(HardwareSecurityPlugin.class);
+ registerPlugin(HotKeyPlugin.class);
super.onCreate(savedInstanceState);
setupStatusBar();
diff --git a/ios/App/App.xcodeproj/project.pbxproj b/ios/App/App.xcodeproj/project.pbxproj
index 66dceb52d..a915683ac 100644
--- a/ios/App/App.xcodeproj/project.pbxproj
+++ b/ios/App/App.xcodeproj/project.pbxproj
@@ -15,6 +15,9 @@
504EC30F1FED79650016851F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 504EC30E1FED79650016851F /* Assets.xcassets */; };
504EC3121FED79650016851F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 504EC3101FED79650016851F /* LaunchScreen.storyboard */; };
50B271D11FEDC1A000F3C39B /* public in Resources */ = {isa = PBXBuildFile; fileRef = 50B271D01FEDC1A000F3C39B /* public */; };
+ 7B8A15032FA8BC67006B631E /* CryptoSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 7B8A15022FA8BC67006B631E /* CryptoSwift */; };
+ 7BC0FFEE2FAA000000000001 /* HotKeyPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BC0FFEF2FAA000000000001 /* HotKeyPlugin.swift */; };
+ 7BECE3022FA7691500F466A5 /* P256K in Frameworks */ = {isa = PBXBuildFile; productRef = 7BECE3012FA7691500F466A5 /* P256K */; settings = {ATTRIBUTES = (Required, ); }; };
83CCE39C02FD0BF8DD39551F /* AppViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A6B2865F76F213517CFCCD1 /* AppViewController.swift */; };
B54E83DC5BCCDB512256423A /* LocalBiometricPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21A34DAD709C71D553F88951 /* LocalBiometricPlugin.swift */; };
C7D4E92A3F8B1C5D00A2B9E1 /* BarcodeScannerPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7D4E92B3F8B1C5D00A2B9E2 /* BarcodeScannerPlugin.swift */; };
@@ -32,6 +35,7 @@
504EC3111FED79650016851F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
504EC3131FED79650016851F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
50B271D01FEDC1A000F3C39B /* public */ = {isa = PBXFileReference; lastKnownFileType = folder; path = public; sourceTree = ""; };
+ 7BC0FFEF2FAA000000000001 /* HotKeyPlugin.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = HotKeyPlugin.swift; sourceTree = ""; };
873F0344C8952CB5585102E0 /* App.entitlements */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.entitlements; path = App.entitlements; sourceTree = ""; };
958DCC722DB07C7200EA8C5F /* debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = debug.xcconfig; path = ../debug.xcconfig; sourceTree = SOURCE_ROOT; };
C7D4E92B3F8B1C5D00A2B9E2 /* BarcodeScannerPlugin.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = BarcodeScannerPlugin.swift; sourceTree = ""; };
@@ -43,6 +47,8 @@
buildActionMask = 2147483647;
files = (
4D22ABE92AF431CB00220026 /* CapApp-SPM in Frameworks */,
+ 7BECE3022FA7691500F466A5 /* P256K in Frameworks */,
+ 7B8A15032FA8BC67006B631E /* CryptoSwift in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -79,6 +85,7 @@
2FAD9762203C412B000D30F8 /* config.xml */,
50B271D01FEDC1A000F3C39B /* public */,
21A34DAD709C71D553F88951 /* LocalBiometricPlugin.swift */,
+ 7BC0FFEF2FAA000000000001 /* HotKeyPlugin.swift */,
C7D4E92B3F8B1C5D00A2B9E2 /* BarcodeScannerPlugin.swift */,
1A6B2865F76F213517CFCCD1 /* AppViewController.swift */,
873F0344C8952CB5585102E0 /* App.entitlements */,
@@ -89,7 +96,6 @@
7BF04FDF2F1F799A005E306E /* Recovered References */ = {
isa = PBXGroup;
children = (
- BFB20C26958B0AB36D108D0E /* Pods-App.release.xcconfig */,
);
name = "Recovered References";
sourceTree = "";
@@ -112,6 +118,8 @@
name = App;
packageProductDependencies = (
4D22ABE82AF431CB00220026 /* CapApp-SPM */,
+ 7BECE3012FA7691500F466A5 /* P256K */,
+ 7B8A15022FA8BC67006B631E /* CryptoSwift */,
);
productName = App;
productReference = 504EC3041FED79650016851F /* App.app */;
@@ -144,6 +152,8 @@
mainGroup = 504EC2FB1FED79650016851F;
packageReferences = (
D4C12C0A2AAA248700AAC8A2 /* XCLocalSwiftPackageReference "CapApp-SPM" */,
+ 7BECE3002FA7691500F466A5 /* XCRemoteSwiftPackageReference "swift-secp256k1" */,
+ 7B8A15012FA8BC67006B631E /* XCRemoteSwiftPackageReference "CryptoSwift" */,
);
productRefGroup = 504EC3051FED79650016851F /* Products */;
projectDirPath = "";
@@ -177,6 +187,7 @@
files = (
504EC3081FED79650016851F /* AppDelegate.swift in Sources */,
B54E83DC5BCCDB512256423A /* LocalBiometricPlugin.swift in Sources */,
+ 7BC0FFEE2FAA000000000001 /* HotKeyPlugin.swift in Sources */,
C7D4E92A3F8B1C5D00A2B9E1 /* BarcodeScannerPlugin.swift in Sources */,
83CCE39C02FD0BF8DD39551F /* AppViewController.swift in Sources */,
);
@@ -393,12 +404,41 @@
};
/* End XCLocalSwiftPackageReference section */
+/* Begin XCRemoteSwiftPackageReference section */
+ 7B8A15012FA8BC67006B631E /* XCRemoteSwiftPackageReference "CryptoSwift" */ = {
+ isa = XCRemoteSwiftPackageReference;
+ repositoryURL = "https://github.com/krzyzanowskim/CryptoSwift";
+ requirement = {
+ kind = upToNextMajorVersion;
+ minimumVersion = 1.10.0;
+ };
+ };
+ 7BECE3002FA7691500F466A5 /* XCRemoteSwiftPackageReference "swift-secp256k1" */ = {
+ isa = XCRemoteSwiftPackageReference;
+ repositoryURL = "https://github.com/21-DOT-DEV/swift-secp256k1.git";
+ requirement = {
+ kind = upToNextMinorVersion;
+ minimumVersion = 0.21.1;
+ };
+ };
+/* End XCRemoteSwiftPackageReference section */
+
/* Begin XCSwiftPackageProductDependency section */
4D22ABE82AF431CB00220026 /* CapApp-SPM */ = {
isa = XCSwiftPackageProductDependency;
package = D4C12C0A2AAA248700AAC8A2 /* XCLocalSwiftPackageReference "CapApp-SPM" */;
productName = "CapApp-SPM";
};
+ 7B8A15022FA8BC67006B631E /* CryptoSwift */ = {
+ isa = XCSwiftPackageProductDependency;
+ package = 7B8A15012FA8BC67006B631E /* XCRemoteSwiftPackageReference "CryptoSwift" */;
+ productName = CryptoSwift;
+ };
+ 7BECE3012FA7691500F466A5 /* P256K */ = {
+ isa = XCSwiftPackageProductDependency;
+ package = 7BECE3002FA7691500F466A5 /* XCRemoteSwiftPackageReference "swift-secp256k1" */;
+ productName = P256K;
+ };
/* End XCSwiftPackageProductDependency section */
};
rootObject = 504EC2FC1FED79650016851F /* Project object */;
diff --git a/ios/App/App/AppViewController.swift b/ios/App/App/AppViewController.swift
index 15f61071b..66c4190d7 100644
--- a/ios/App/App/AppViewController.swift
+++ b/ios/App/App/AppViewController.swift
@@ -6,6 +6,7 @@ class AppViewController: CAPBridgeViewController {
override open func capacitorDidLoad() {
bridge?.registerPluginInstance(LocalBiometricPlugin())
bridge?.registerPluginInstance(BarcodeScannerPlugin())
+ bridge?.registerPluginInstance(HotKeyPlugin())
bridge?.registerPluginInstance(MidenNativeProverPlugin())
}
}
diff --git a/ios/App/App/HotKeyPlugin.swift b/ios/App/App/HotKeyPlugin.swift
new file mode 100644
index 000000000..f34cb03a9
--- /dev/null
+++ b/ios/App/App/HotKeyPlugin.swift
@@ -0,0 +1,413 @@
+import Foundation
+import Capacitor
+import LocalAuthentication
+import Security
+import os.log
+import P256K
+import CryptoSwift
+
+// Per-account Guardian "hot" signing key (3-key migration, Phase 4).
+// Split out of LocalBiometricPlugin so this plugin owns one concern: the SE-
+// wrapped secp256k1 hot key used for transaction signing. The Keychain /
+// hardware-key paths stay in LocalBiometric.
+//
+// Storage layout:
+// - Per-account SE-backed P-256 key tagged "com.miden.wallet.hot."
+// - Returned ciphertext is ":" so signWith /
+// deleteWith can recover the tag from the blob alone.
+
+private let logger = OSLog(subsystem: "com.miden.wallet", category: "HotKey")
+
+private let kHotKeyTagPrefix = "com.miden.wallet.hot."
+
+@objc(HotKeyPlugin)
+public class HotKeyPlugin: CAPPlugin, CAPBridgedPlugin {
+ public let identifier = "HotKeyPlugin"
+ public let jsName = "HotKey"
+ public let pluginMethods: [CAPPluginMethod] = [
+ CAPPluginMethod(name: "generateHotKey", returnType: CAPPluginReturnPromise),
+ CAPPluginMethod(name: "signWithHotKey", returnType: CAPPluginReturnPromise),
+ CAPPluginMethod(name: "deleteHotKey", returnType: CAPPluginReturnPromise),
+ CAPPluginMethod(name: "revealHotKey", returnType: CAPPluginReturnPromise)
+ ]
+
+ @objc func generateHotKey(_ call: CAPPluginCall) {
+ os_log("[HotKey] generateHotKey called", log: logger, type: .debug)
+
+ // 1. Random k256 secret
+ var secretBytes = Data(count: 32)
+ let rngStatus = secretBytes.withUnsafeMutableBytes { raw -> Int32 in
+ guard let base = raw.baseAddress else { return errSecParam }
+ return SecRandomCopyBytes(kSecRandomDefault, 32, base)
+ }
+ guard rngStatus == errSecSuccess else {
+ call.reject("Failed to generate hot-key secret: \(rngStatus)")
+ return
+ }
+
+ // 2. Derive compressed k256 public key (33 bytes: 0x02/0x03 parity
+ // prefix + 32-byte x). Miden SDK's PublicKey.deserialize expects
+ // the compressed form; this matches what jsFallback.ts emits via
+ // AuthSecretKey.publicKey().serialize().slice(1). P256K's default
+ // format is .compressed, so `dataRepresentation` is already the
+ // 33-byte form — no stripping needed.
+ let publicKeyHex: String
+ do {
+ let pk = try P256K.Signing.PrivateKey(dataRepresentation: secretBytes)
+ let rawPub = pk.publicKey.dataRepresentation
+ publicKeyHex = rawPub.map { String(format: "%02x", $0) }.joined()
+ } catch {
+ zeroBytes(&secretBytes)
+ call.reject("Failed to derive hot-key public key: \(error.localizedDescription)")
+ return
+ }
+
+ // 3. Random 16-byte tag suffix; full Keychain tag is prefix+suffix.
+ var tagSuffix = Data(count: 16)
+ let tagStatus = tagSuffix.withUnsafeMutableBytes { raw -> Int32 in
+ guard let base = raw.baseAddress else { return errSecParam }
+ return SecRandomCopyBytes(kSecRandomDefault, 16, base)
+ }
+ guard tagStatus == errSecSuccess else {
+ zeroBytes(&secretBytes)
+ call.reject("Failed to generate hot-key tag: \(tagStatus)")
+ return
+ }
+ let tagSuffixB64 = tagSuffix.base64EncodedString()
+ let fullTag = kHotKeyTagPrefix + tagSuffixB64
+ guard let fullTagData = fullTag.data(using: .utf8) else {
+ zeroBytes(&secretBytes)
+ call.reject("Failed to encode hot-key tag")
+ return
+ }
+
+ // 4. Create the SE-backed P-256 key. .privateKeyUsage triggers Face ID
+ // only when the private key is used (i.e. SecKeyCreateDecryptedData
+ // in signWithHotKey), not at create time.
+ // THREAT MODEL: we intentionally do NOT add `.biometryCurrentSet`.
+ // That flag would invalidate the SE key whenever the biometric
+ // enrollment changes (a face/finger added or re-enrolled), forcing the
+ // user to re-activate the hot key. We accept "any enrolled biometric
+ // can sign" in exchange for not bricking the hot key on an enrollment
+ // change; the key never leaves the Secure Enclave either way. Switch
+ // to `.biometryCurrentSet` (with a re-activation migration) if a
+ // stricter "enrollment change = re-activate" posture is required.
+ var accessError: Unmanaged?
+ guard let accessControl = SecAccessControlCreateWithFlags(
+ kCFAllocatorDefault,
+ kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
+ .privateKeyUsage,
+ &accessError
+ ) else {
+ zeroBytes(&secretBytes)
+ let msg = accessError?.takeRetainedValue().localizedDescription ?? "unknown"
+ call.reject("Failed to create access control: \(msg)")
+ return
+ }
+
+ var seKeyAttributes: [String: Any] = [
+ kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom,
+ kSecAttrKeySizeInBits as String: 256,
+ kSecPrivateKeyAttrs as String: [
+ kSecAttrIsPermanent as String: true,
+ kSecAttrApplicationTag as String: fullTagData,
+ kSecAttrAccessControl as String: accessControl
+ ]
+ ]
+ // On simulator the host SE is unavailable; without the token attribute
+ // the key falls back to software-backed but the same APIs work, which
+ // lets us iterate against the iPhone simulator. Real devices require
+ // SE — same trade-off as generateHardwareKey.
+ #if !targetEnvironment(simulator)
+ seKeyAttributes[kSecAttrTokenID as String] = kSecAttrTokenIDSecureEnclave
+ #endif
+
+ var keyError: Unmanaged?
+ guard let sePrivateKey = SecKeyCreateRandomKey(seKeyAttributes as CFDictionary, &keyError) else {
+ zeroBytes(&secretBytes)
+ let msg = keyError?.takeRetainedValue().localizedDescription ?? "unknown"
+ call.reject("Failed to generate hot-key SE key: \(msg)")
+ return
+ }
+ guard let sePublicKey = SecKeyCopyPublicKey(sePrivateKey) else {
+ zeroBytes(&secretBytes)
+ // Best-effort cleanup of the orphan SE key we just created.
+ SecItemDelete([
+ kSecClass as String: kSecClassKey,
+ kSecAttrApplicationTag as String: fullTagData
+ ] as CFDictionary)
+ call.reject("Failed to obtain hot-key SE public key")
+ return
+ }
+
+ // 5. ECIES-encrypt the k256 secret to the SE public key. Apple's
+ // eciesEncryptionStandardX963SHA256AESGCM produces a self-describing
+ // blob (ephem pubkey || iv || ct || tag), opaque to us.
+ var encError: Unmanaged?
+ guard let wrapped = SecKeyCreateEncryptedData(
+ sePublicKey,
+ .eciesEncryptionStandardX963SHA256AESGCM,
+ secretBytes as CFData,
+ &encError
+ ) as Data? else {
+ zeroBytes(&secretBytes)
+ SecItemDelete([
+ kSecClass as String: kSecClassKey,
+ kSecAttrApplicationTag as String: fullTagData
+ ] as CFDictionary)
+ let msg = encError?.takeRetainedValue().localizedDescription ?? "unknown"
+ call.reject("Failed to wrap hot-key secret: \(msg)")
+ return
+ }
+
+ zeroBytes(&secretBytes)
+
+ // 6. Pack into ":" so signWithHotKey can
+ // recover the SE key tag from the ciphertext alone.
+ let packed = "\(tagSuffixB64):\(wrapped.base64EncodedString())"
+ os_log("[HotKey] generateHotKey success", log: logger, type: .debug)
+ call.resolve([
+ "ciphertext": packed,
+ "publicKeyHex": publicKeyHex
+ ])
+ }
+
+ /// Unwrap the hot-key secret inside the SE (triggers Face ID), Keccak-256
+ /// the supplied 32-byte word, ECDSA-sign (recoverable) over secp256k1,
+ /// and return r||s||v as 0x-prefixed hex (65 bytes). The unwrapped secret
+ /// is zeroed before returning.
+ @objc func signWithHotKey(_ call: CAPPluginCall) {
+ os_log("[HotKey] signWithHotKey called", log: logger, type: .debug)
+
+ guard let ciphertext = call.getString("ciphertext"),
+ let digestHex = call.getString("digestHex") else {
+ call.reject("Missing 'ciphertext' or 'digestHex' parameter")
+ return
+ }
+
+ // 1. Split tag from payload.
+ let parts = ciphertext.split(separator: ":", maxSplits: 1, omittingEmptySubsequences: false)
+ guard parts.count == 2,
+ let payload = Data(base64Encoded: String(parts[1])) else {
+ call.reject("Malformed hot-key ciphertext")
+ return
+ }
+ let fullTag = kHotKeyTagPrefix + String(parts[0])
+ guard let fullTagData = fullTag.data(using: .utf8) else {
+ call.reject("Failed to encode hot-key tag")
+ return
+ }
+
+ // 2. Decode the digest (caller passes it 0x-prefixed, matching
+ // Word.toHex()). Must be 32 bytes — Miden Words are 4 felts × 8.
+ // Use CryptoSwift's `Data(hex:)` so the same lib that does the
+ // Keccak hashes the bytes it parsed.
+ let cleanedHex = digestHex.hasPrefix("0x") ? String(digestHex.dropFirst(2)) : digestHex
+ let digestBytes = Data(hex: cleanedHex)
+ guard digestBytes.count == 32 else {
+ call.reject("Hot-key digest must be 32 hex bytes")
+ return
+ }
+
+ // 3. Look up the SE private key by tag.
+ let query: [String: Any] = [
+ kSecClass as String: kSecClassKey,
+ kSecAttrApplicationTag as String: fullTagData,
+ kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom,
+ kSecReturnRef as String: true
+ ]
+ var keyRef: CFTypeRef?
+ let lookupStatus = SecItemCopyMatching(query as CFDictionary, &keyRef)
+ guard lookupStatus == errSecSuccess, let foundKey = keyRef else {
+ os_log("[HotKey] signWithHotKey: SE key not found %{public}d", log: logger, type: .error, lookupStatus)
+ if lookupStatus == errSecUserCanceled {
+ call.reject("Authentication cancelled", "USER_CANCELLED")
+ } else if lookupStatus == errSecAuthFailed {
+ call.reject("Authentication failed", "AUTH_FAILED")
+ } else {
+ call.reject("Hot-key SE key not found: \(lookupStatus)")
+ }
+ return
+ }
+ // Conditional cast: a corrupted/foreign Keychain item at this tag would
+ // crash the app process on a force-cast.
+ guard let sePrivateKey = foundKey as? SecKey else {
+ call.reject("Hot-key Keychain item is not a SecKey")
+ return
+ }
+
+ // 4. SecKeyCreateDecryptedData triggers Face ID via .privateKeyUsage
+ // on the SE key.
+ var decError: Unmanaged?
+ guard var unwrapped = SecKeyCreateDecryptedData(
+ sePrivateKey,
+ .eciesEncryptionStandardX963SHA256AESGCM,
+ payload as CFData,
+ &decError
+ ) as Data? else {
+ let nsError = decError?.takeRetainedValue() as? NSError
+ let msg = nsError?.localizedDescription ?? "unknown"
+ os_log("[HotKey] signWithHotKey decrypt failed: %{public}@", log: logger, type: .error, msg)
+ if nsError?.domain == LAError.errorDomain && nsError?.code == LAError.userCancel.rawValue {
+ call.reject("Authentication cancelled", "USER_CANCELLED")
+ } else if nsError?.domain == LAError.errorDomain && nsError?.code == LAError.authenticationFailed.rawValue {
+ call.reject("Authentication failed", "AUTH_FAILED")
+ } else {
+ call.reject("Failed to unwrap hot-key secret: \(msg)")
+ }
+ return
+ }
+ guard unwrapped.count == 32 else {
+ zeroBytes(&unwrapped)
+ call.reject("Unwrapped hot-key has wrong length")
+ return
+ }
+
+ // 5. Keccak-256 the word, then ECDSA-sign (recoverable) with secp256k1.
+ // Use P256K.Recovery (not Signing) so we can pull the recovery id.
+ // `compactRepresentation` returns (signature: Data, recoveryId: Int32):
+ // 64-byte r||s plus the 0/1 recovery byte. We emit r||s||v (65 bytes)
+ // where v is the raw recovery id. If the consumer expects Ethereum-
+ // style v, add 27 on the JS side — keeping it raw here so we don't
+ // bake a chain convention into the native plugin.
+ let keccakDigest = digestBytes.sha3(.keccak256)
+ let signatureHex: String
+ do {
+ let pk = try P256K.Recovery.PrivateKey(dataRepresentation: unwrapped)
+ let digestBuffer = HashDigest(Array(keccakDigest))
+ let sig = try pk.signature(for: digestBuffer)
+ let compact = try sig.compactRepresentation
+ let v = UInt8(truncatingIfNeeded: compact.recoveryId)
+ let rs = compact.signature.map { String(format: "%02x", $0) }.joined()
+ signatureHex = "0x" + rs + String(format: "%02x", v)
+ } catch {
+ zeroBytes(&unwrapped)
+ call.reject("Hot-key ECDSA sign failed: \(error.localizedDescription)")
+ return
+ }
+
+ zeroBytes(&unwrapped)
+
+ os_log("[HotKey] signWithHotKey success", log: logger, type: .debug)
+ call.resolve(["signatureHex": signatureHex])
+ }
+
+ /// Unwrap the hot-key secret inside the SE (triggers Face ID) and return
+ /// the raw 32-byte secp256k1 secret as hex. Used by Settings → Reveal Hot
+ /// Key. Same SE/ECIES unwrap path as `signWithHotKey`, minus the actual
+ /// signing step. The unwrapped secret is zeroed before returning.
+ @objc func revealHotKey(_ call: CAPPluginCall) {
+ os_log("[HotKey] revealHotKey called", log: logger, type: .debug)
+
+ guard let ciphertext = call.getString("ciphertext") else {
+ call.reject("Missing 'ciphertext' parameter")
+ return
+ }
+
+ let parts = ciphertext.split(separator: ":", maxSplits: 1, omittingEmptySubsequences: false)
+ guard parts.count == 2,
+ let payload = Data(base64Encoded: String(parts[1])) else {
+ call.reject("Malformed hot-key ciphertext")
+ return
+ }
+ let fullTag = kHotKeyTagPrefix + String(parts[0])
+ guard let fullTagData = fullTag.data(using: .utf8) else {
+ call.reject("Failed to encode hot-key tag")
+ return
+ }
+
+ let query: [String: Any] = [
+ kSecClass as String: kSecClassKey,
+ kSecAttrApplicationTag as String: fullTagData,
+ kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom,
+ kSecReturnRef as String: true
+ ]
+ var keyRef: CFTypeRef?
+ let lookupStatus = SecItemCopyMatching(query as CFDictionary, &keyRef)
+ guard lookupStatus == errSecSuccess, let foundKey = keyRef else {
+ os_log("[HotKey] revealHotKey: SE key not found %{public}d", log: logger, type: .error, lookupStatus)
+ if lookupStatus == errSecUserCanceled {
+ call.reject("Authentication cancelled", "USER_CANCELLED")
+ } else if lookupStatus == errSecAuthFailed {
+ call.reject("Authentication failed", "AUTH_FAILED")
+ } else {
+ call.reject("Hot-key SE key not found: \(lookupStatus)")
+ }
+ return
+ }
+ // Conditional cast: avoid crashing on a corrupted/foreign Keychain item.
+ guard let sePrivateKey = foundKey as? SecKey else {
+ call.reject("Hot-key Keychain item is not a SecKey")
+ return
+ }
+
+ var decError: Unmanaged?
+ guard var unwrapped = SecKeyCreateDecryptedData(
+ sePrivateKey,
+ .eciesEncryptionStandardX963SHA256AESGCM,
+ payload as CFData,
+ &decError
+ ) as Data? else {
+ let nsError = decError?.takeRetainedValue() as? NSError
+ let msg = nsError?.localizedDescription ?? "unknown"
+ os_log("[HotKey] revealHotKey decrypt failed: %{public}@", log: logger, type: .error, msg)
+ if nsError?.domain == LAError.errorDomain && nsError?.code == LAError.userCancel.rawValue {
+ call.reject("Authentication cancelled", "USER_CANCELLED")
+ } else if nsError?.domain == LAError.errorDomain && nsError?.code == LAError.authenticationFailed.rawValue {
+ call.reject("Authentication failed", "AUTH_FAILED")
+ } else {
+ call.reject("Failed to unwrap hot-key secret: \(msg)")
+ }
+ return
+ }
+ guard unwrapped.count == 32 else {
+ zeroBytes(&unwrapped)
+ call.reject("Unwrapped hot-key has wrong length")
+ return
+ }
+
+ let secretKeyHex = unwrapped.map { String(format: "%02x", $0) }.joined()
+ zeroBytes(&unwrapped)
+
+ os_log("[HotKey] revealHotKey success", log: logger, type: .debug)
+ call.resolve(["secretKeyHex": secretKeyHex])
+ }
+
+ /// Delete the per-account SE hot key. Idempotent — a missing key resolves
+ /// successfully so callers can call this during account deletion without
+ /// branching on existence.
+ @objc func deleteHotKey(_ call: CAPPluginCall) {
+ os_log("[HotKey] deleteHotKey called", log: logger, type: .debug)
+
+ guard let ciphertext = call.getString("ciphertext") else {
+ call.reject("Missing 'ciphertext' parameter")
+ return
+ }
+
+ let parts = ciphertext.split(separator: ":", maxSplits: 1, omittingEmptySubsequences: false)
+ guard parts.count >= 1 else {
+ call.reject("Malformed hot-key ciphertext")
+ return
+ }
+ let fullTag = kHotKeyTagPrefix + String(parts[0])
+ guard let fullTagData = fullTag.data(using: .utf8) else {
+ call.reject("Failed to encode hot-key tag")
+ return
+ }
+
+ let status = SecItemDelete([
+ kSecClass as String: kSecClassKey,
+ kSecAttrApplicationTag as String: fullTagData
+ ] as CFDictionary)
+ os_log("[HotKey] deleteHotKey status: %{public}d", log: logger, type: .debug, status)
+ call.resolve()
+ }
+
+ private func zeroBytes(_ data: inout Data) {
+ data.withUnsafeMutableBytes { raw in
+ if let base = raw.baseAddress {
+ memset_s(base, raw.count, 0, raw.count)
+ }
+ }
+ }
+}
diff --git a/ios/App/App/LocalBiometricPlugin.swift b/ios/App/App/LocalBiometricPlugin.swift
index 8ae2cb3ad..f90a1f2b5 100644
--- a/ios/App/App/LocalBiometricPlugin.swift
+++ b/ios/App/App/LocalBiometricPlugin.swift
@@ -594,4 +594,5 @@ public class LocalBiometricPlugin: CAPPlugin, CAPBridgedPlugin {
return nil
}
}
+
}
diff --git a/ios/App/CapApp-SPM/Package.resolved b/ios/App/CapApp-SPM/Package.resolved
new file mode 100644
index 000000000..6c575b00a
--- /dev/null
+++ b/ios/App/CapApp-SPM/Package.resolved
@@ -0,0 +1,23 @@
+{
+ "pins" : [
+ {
+ "identity" : "capacitor-swift-pm",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/ionic-team/capacitor-swift-pm.git",
+ "state" : {
+ "revision" : "3e7ccfb8ab4d321ffcbd4e490234758e139f8a09",
+ "version" : "8.0.1"
+ }
+ },
+ {
+ "identity" : "ion-ios-filesystem",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/ionic-team/ion-ios-filesystem.git",
+ "state" : {
+ "revision" : "0d81e26e828ff9582807e2339112cedf2e0fab85",
+ "version" : "1.1.2"
+ }
+ }
+ ],
+ "version" : 2
+}
diff --git a/jest.config.ts b/jest.config.ts
index 4babe2ac2..94c4c56bb 100644
--- a/jest.config.ts
+++ b/jest.config.ts
@@ -65,12 +65,20 @@ export default {
transform: {
'.+\\.(ts|tsx|js|mjs)$': '@swc/jest'
},
- transformIgnorePatterns: [
- '/node_modules/(?!(p-queue|p-timeout|eventemitter3|date-fns|dexie)/)'
- ],
+ transformIgnorePatterns: ['/node_modules/(?!(p-queue|p-timeout|eventemitter3|date-fns|dexie)/)'],
moduleFileExtensions: ['ts', 'tsx', 'js'],
- modulePathIgnorePatterns: ['/sdk-debug/'],
- testPathIgnorePatterns: ['/playwright/', '/mobile-e2e/'],
+ // Exclude git worktrees: they hold full copies of the repo, so without this a
+ // plain `jest` run discovers their stale test files and emits haste-map
+ // duplicate-mock collisions (spurious local failures). CI checks out clean, so
+ // this only matters for local runs.
+ modulePathIgnorePatterns: ['/sdk-debug/', '/.worktrees/', '/.claude/'],
+ testPathIgnorePatterns: [
+ '/playwright/',
+ '/mobile-e2e/',
+ '/ios/App/build/',
+ '/.worktrees/',
+ '/.claude/'
+ ],
setupFiles: ['dotenv/config', '@serh11p/jest-webextension-mock', 'fake-indexeddb/auto'],
setupFilesAfterEnv: ['./jest.setup.js']
};
diff --git a/playwright/e2e/android/helpers/android-wallet-page.ts b/playwright/e2e/android/helpers/android-wallet-page.ts
index 8298e83b9..66454eb02 100644
--- a/playwright/e2e/android/helpers/android-wallet-page.ts
+++ b/playwright/e2e/android/helpers/android-wallet-page.ts
@@ -1,5 +1,5 @@
import type { TimelineRecorder } from '../../harness/timeline-recorder';
-import type { WalletPage } from '../../helpers/wallet-page';
+import type { GuardianAuthInfo, WalletPage } from '../../helpers/wallet-page';
import type { CdpSession } from './cdp-bridge';
import type { EmulatorControl } from './emulator-control';
@@ -409,6 +409,40 @@ export class AndroidWalletPage implements WalletPage {
);
}
+ /**
+ * Read a Guardian account's on-chain auth structure (overall threshold,
+ * signer commitments, per-procedure thresholds). Same contract and body as
+ * the iOS POM — the __TEST_GUARDIAN_AUTH__ hook is async (awaits
+ * getOrCreateMultisigService + a time-bounded sync), so it runs under the
+ * async CDP atom whose callback convention matches iOS
+ * (`arguments[arguments.length - 1]`).
+ */
+ async getGuardianAuthInfo(accountPublicKey: string): Promise {
+ return this.cdp.evalAsync(
+ `var cb = arguments[arguments.length - 1];
+ var fn = globalThis.__TEST_GUARDIAN_AUTH__;
+ if (typeof fn !== 'function') {
+ cb({
+ threshold: NaN,
+ signerCommitments: [],
+ procedureThresholds: {},
+ error: '__TEST_GUARDIAN_AUTH__ unavailable (needs MIDEN_E2E_TEST build)'
+ });
+ return;
+ }
+ Promise.resolve(fn(${JSON.stringify(accountPublicKey)}))
+ .then(function (r) { cb(r); })
+ .catch(function (e) {
+ cb({
+ threshold: NaN,
+ signerCommitments: [],
+ procedureThresholds: {},
+ error: String(e && e.message ? e.message : e)
+ });
+ });`
+ );
+ }
+
private async triggerNavbarAction(timeoutMs: number): Promise {
const start = Date.now();
while (Date.now() - start < timeoutMs) {
diff --git a/playwright/e2e/helpers/wallet-page.ts b/playwright/e2e/helpers/wallet-page.ts
index a38c3f31e..4a2069307 100644
--- a/playwright/e2e/helpers/wallet-page.ts
+++ b/playwright/e2e/helpers/wallet-page.ts
@@ -48,6 +48,14 @@ export interface WalletPage {
* the change takes effect on the next render — no reload needed.
*/
setDelegateProofEnabled(enabled: boolean): Promise;
+ /**
+ * On-chain auth structure of a Guardian account (overall threshold, signer
+ * commitments, per-procedure thresholds) — for asserting the 3-key shape
+ * (e.g. `update_guardian === 2`, two signers) which balance checks can't see.
+ * Shared across Chrome (page.evaluate) and iOS (CDP evalAsync) so the same
+ * assertion runs on both platforms.
+ */
+ getGuardianAuthInfo(accountPublicKey: string): Promise;
}
/**
@@ -76,12 +84,21 @@ export interface ChromeWalletPageApi extends WalletPage, IdbDumpSource {
}>;
/** Full dump of chrome.storage.local — end-of-run forensic snapshot. */
dumpChromeStorage(): Promise>;
+ // getGuardianAuthInfo is declared on the shared WalletPage interface (above)
+ // so the iOS POM implements it too — the 3-key auth assertion runs on both.
// IndexedDB forensics (listIndexedDBStores / dumpIndexedDBStore) come from
// IdbDumpSource — driven store-at-a-time by streamIndexedDBToFile so a long
// run's dump can't OOM the page. This is where the Miden SDK keeps per-tx
// commit status — the ground truth for "did this tx land?".
}
+export interface GuardianAuthInfo {
+ threshold: number;
+ signerCommitments: string[];
+ procedureThresholds: Record;
+ error?: string;
+}
+
/**
* Page Object Model for a single wallet extension instance.
* Encapsulates all UI interactions, reusing selectors from popup-smoke.spec.ts.
@@ -627,6 +644,22 @@ export class ChromeWalletPage implements ChromeWalletPageApi {
}
}
+ async getGuardianAuthInfo(accountPublicKey: string): Promise {
+ return this.page.evaluate(async (pk: string) => {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ const fn = (globalThis as any).__TEST_GUARDIAN_AUTH__;
+ if (!fn) {
+ return {
+ threshold: NaN,
+ signerCommitments: [],
+ procedureThresholds: {},
+ error: '__TEST_GUARDIAN_AUTH__ unavailable (needs MIDEN_E2E_TEST build)'
+ };
+ }
+ return await fn(pk);
+ }, accountPublicKey);
+ }
+
/**
* Enumerate every (db, store) on the extension origin — cheap, loads no row
* data. Pairs with `dumpIndexedDBStore` so `streamIndexedDBToFile` can pull
diff --git a/playwright/e2e/ios/helpers/ios-wallet-page.ts b/playwright/e2e/ios/helpers/ios-wallet-page.ts
index 7a98037d6..c3de28376 100644
--- a/playwright/e2e/ios/helpers/ios-wallet-page.ts
+++ b/playwright/e2e/ios/helpers/ios-wallet-page.ts
@@ -1,5 +1,5 @@
import type { TimelineRecorder } from '../../harness/timeline-recorder';
-import type { WalletPage } from '../../helpers/wallet-page';
+import type { GuardianAuthInfo, WalletPage } from '../../helpers/wallet-page';
import type { CdpSession } from './cdp-bridge';
import type { SimulatorControl } from './simulator-control';
@@ -593,6 +593,42 @@ export class IosWalletPage implements WalletPage {
);
}
+ /**
+ * Read a Guardian account's on-chain auth structure (overall threshold,
+ * signer commitments, per-procedure thresholds). Calls the same
+ * __TEST_GUARDIAN_AUTH__ hook the Chrome POM uses, but over the async CDP
+ * atom: the hook awaits getOrCreateMultisigService + a best-effort
+ * (time-bounded) sync, so it returns a Promise and must run under
+ * execute_async_script. The hook itself caps its internal sync at 8s, so the
+ * 30s evalAsync budget is comfortable even when the background sync holds the
+ * WASM lock.
+ */
+ async getGuardianAuthInfo(accountPublicKey: string): Promise {
+ return this.cdp.evalAsync(
+ `var cb = arguments[arguments.length - 1];
+ var fn = globalThis.__TEST_GUARDIAN_AUTH__;
+ if (typeof fn !== 'function') {
+ cb({
+ threshold: NaN,
+ signerCommitments: [],
+ procedureThresholds: {},
+ error: '__TEST_GUARDIAN_AUTH__ unavailable (needs MIDEN_E2E_TEST build)'
+ });
+ return;
+ }
+ Promise.resolve(fn(${JSON.stringify(accountPublicKey)}))
+ .then(function (r) { cb(r); })
+ .catch(function (e) {
+ cb({
+ threshold: NaN,
+ signerCommitments: [],
+ procedureThresholds: {},
+ error: String(e && e.message ? e.message : e)
+ });
+ });`
+ );
+ }
+
/**
* Fire the currently-registered native navbar action on mobile. The
* wallet's native iOS overlay (MidenNavbarOverlayWindow) lives in a
diff --git a/playwright/e2e/ios/tests/guardian-send-consume.ios.spec.ts b/playwright/e2e/ios/tests/guardian-send-consume.ios.spec.ts
index ca7244eb2..96513c869 100644
--- a/playwright/e2e/ios/tests/guardian-send-consume.ios.spec.ts
+++ b/playwright/e2e/ios/tests/guardian-send-consume.ios.spec.ts
@@ -69,6 +69,21 @@ test.describe('Guardian account - consume + send', () => {
}
);
+ await steps.step('verify_guardian_auth_structure_a', async () => {
+ // First on-chain AUTH assertion in the iOS harness (mirrors the Chrome
+ // spec): a 3-key Guardian account must carry two signers ([hot, cold])
+ // and the `update_guardian` procedure hardened to threshold 2 — both set
+ // at creation. Runs after the consume has committed the account so the
+ // cached MultisigService read reflects the on-chain shape. The read goes
+ // through the async CDP atom (the hook awaits a time-bounded sync), which
+ // CDP can see — unlike the native navbar, which is why this is a WebView
+ // read rather than a UI check.
+ const auth = await walletA.getGuardianAuthInfo(addressA!);
+ expect(auth.error, `guardian auth read failed: ${auth.error}`).toBeUndefined();
+ expect(auth.signerCommitments.length, 'fresh 3-key account should have 2 signers (hot, cold)').toBe(2);
+ expect(auth.procedureThresholds.update_guardian, 'update_guardian must be hardened to threshold 2').toBe(2);
+ });
+
await steps.step(
'send_guardian_a_to_b',
async () => {
diff --git a/playwright/e2e/tests/guardian-send-consume.spec.ts b/playwright/e2e/tests/guardian-send-consume.spec.ts
index 04d184aad..7902745e7 100644
--- a/playwright/e2e/tests/guardian-send-consume.spec.ts
+++ b/playwright/e2e/tests/guardian-send-consume.spec.ts
@@ -54,6 +54,18 @@ test.describe('Guardian account - consume + send', () => {
}
);
+ await steps.step('verify_guardian_auth_structure_a', async () => {
+ // First on-chain AUTH assertion in the harness (balance checks can't see
+ // this): a fresh 3-key Guardian account must carry two signers ([hot,
+ // cold]) and the `update_guardian` procedure hardened to threshold 2 —
+ // both set at creation. The same reader verifies the migrated/activated
+ // path in the (P1) migration spec.
+ const auth = await walletA.getGuardianAuthInfo(addressA!);
+ expect(auth.error, `guardian auth read failed: ${auth.error}`).toBeUndefined();
+ expect(auth.signerCommitments.length, 'fresh 3-key account should have 2 signers (hot, cold)').toBe(2);
+ expect(auth.procedureThresholds.update_guardian, 'update_guardian must be hardened to threshold 2').toBe(2);
+ });
+
await steps.step(
'consume_notes_guardian_a',
async () => {
diff --git a/public/_locales/de/messages.json b/public/_locales/de/messages.json
index fdc8dffc4..3369028b0 100644
--- a/public/_locales/de/messages.json
+++ b/public/_locales/de/messages.json
@@ -1490,6 +1490,10 @@
"message": "Teilen Sie es nicht mit anderen.",
"englishSource": "Do not share with anyone."
},
+ "seedPhraseRecoveryCaption": {
+ "message": "Ihr seed phrase ist Ihr Wiederherstellungsschlüssel – er wird zum Rotieren von Geräteschlüsseln verwendet, nicht für alltägliche Transaktionen.",
+ "englishSource": "Your seed phrase is your recovery key — used to rotate device keys, not for everyday transactions."
+ },
"select": {
"message": "Auswählen",
"note": "Onboarding",
@@ -1672,6 +1676,14 @@
"message": "Richten Sie Ihr Guardian-Konto auf einen neuen Guardian-Endpunkt. Der Wechsel wird als On-Chain-Vorschlag signiert.",
"englishSource": "Point your Guardian account at a new guardian endpoint. The switch is signed as an on-chain proposal."
},
+ "switchGuardianConfirmation": {
+ "message": "Dies wird mit Ihrem Wiederherstellungsschlüssel (Kaltschlüssel) signiert und von Ihrem aktuellen Vormund mitunterzeichnet. Ihr Guthaben und Ihr Konto bleiben gleich. Weitermachen?",
+ "englishSource": "This is signed with your recovery (cold) key and co-signed by your current guardian. Your funds and account stay the same. Continue?"
+ },
+ "confirmSwitchGuardian": {
+ "message": "Wechsel bestätigen",
+ "englishSource": "Confirm switch"
+ },
"switchingGuardian": {
"message": "Vormund wechseln...",
"englishSource": "Switching guardian..."
@@ -1684,6 +1696,78 @@
"message": "Das ist bereits Ihr aktueller Vormund.",
"englishSource": "That's already your current guardian."
},
+ "replaceHotKey": {
+ "message": "Geräteschlüssel drehen",
+ "englishSource": "Rotate device key"
+ },
+ "replaceHotKeyDescription": {
+ "message": "Generieren Sie einen neuen Geräteschlüssel (Hotkey) und ersetzen Sie den aktuellen in der Kette. Nützlich, wenn Sie den Verdacht haben, dass Ihr Gerät kompromittiert ist.",
+ "englishSource": "Generate a new device (hot) key and replace the current one on-chain. Useful if you suspect your device is compromised."
+ },
+ "replaceHotKeyConfirmation": {
+ "message": "Dies wird mit Ihrem Wiederherstellungsschlüssel (Kaltschlüssel) signiert. Ihr seed phrase ist nicht erforderlich und Ihr Guthaben und Ihr Konto bleiben gleich. Weitermachen?",
+ "englishSource": "This is signed with your recovery (cold) key. Your seed phrase is not required, and your funds and account stay the same. Continue?"
+ },
+ "confirmReplaceHotKey": {
+ "message": "Drehung bestätigen",
+ "englishSource": "Confirm rotation"
+ },
+ "hotKeyRotated": {
+ "message": "Geräteschlüssel erfolgreich gedreht.",
+ "englishSource": "Device key rotated successfully."
+ },
+ "activateHotKeyBannerTitle": {
+ "message": "Geräteschlüssel aktivieren",
+ "englishSource": "Activate device key"
+ },
+ "activateHotKeyBannerBody": {
+ "message": "Generieren Sie einen Geräteschlüssel für dieses wiederhergestellte Konto. Vor dem Senden erforderlich.",
+ "englishSource": "Generate a device key for this recovered account. Required before sending."
+ },
+ "activateHotKeyBannerCta": {
+ "message": "Aktivieren",
+ "englishSource": "Activate"
+ },
+ "activateHotKeyBannerError": {
+ "message": "Der Geräteschlüssel konnte nicht aktiviert werden. Versuchen Sie es erneut.",
+ "englishSource": "Failed to activate device key. Try again."
+ },
+ "revealHotKey": {
+ "message": "Geräteschlüssel anzeigen",
+ "englishSource": "Reveal device key"
+ },
+ "revealHotKeyDescription": {
+ "message": "Ihr Geräteschlüssel (Hotkey) kann unter „Einstellungen“ → „Geräteschlüssel drehen“ gedreht werden. Es ist also weniger riskant, ihn zu enthüllen als Ihren seed phrase – aber jeder, der ihn besitzt, kann Transaktionen signieren, bis Sie ihn drehen.",
+ "englishSource": "Your device (hot) key is rotatable from Settings → Rotate Device Key, so revealing it is lower-stakes than your seed phrase — but anyone who holds it can sign transactions until you rotate."
+ },
+ "hotPrivateKey": {
+ "message": "Privater Geräteschlüssel",
+ "englishSource": "Device Private Key"
+ },
+ "hotPublicKeyLabel": {
+ "message": "Öffentlicher Geräteschlüssel",
+ "englishSource": "Device Public Key"
+ },
+ "coldPrivateKey": {
+ "message": "Privater Wiederherstellungsschlüssel (kalt).",
+ "englishSource": "Recovery (Cold) Private Key"
+ },
+ "coldPublicKeyLabel": {
+ "message": "Öffentlicher Wiederherstellungsschlüssel (kalt).",
+ "englishSource": "Recovery (Cold) Public Key"
+ },
+ "guardianKeysRevealDescription": {
+ "message": "Ihr Guardian-Konto ist durch drei Schlüssel geschützt: einen Geräteschlüssel (Hotschlüssel) für routinemäßige Signaturen, einen von Ihrem seed phrase abgeleiteten Wiederherstellungsschlüssel (Kaltschlüssel) für Kontoverwaltungsvorgänge und einen externen Guardian-Mitunterzeichner. Nachfolgend finden Sie Ihren kalten privaten Schlüssel sowie beide öffentlichen Schlüssel. Um den privaten Schlüssel des Geräts anzuzeigen, verwenden Sie Einstellungen → Geräteschlüssel anzeigen.",
+ "englishSource": "Your Guardian account is protected by three keys: a device (hot) key for routine signing, a recovery (cold) key derived from your seed phrase for account-management operations, and an external guardian co-signer. Below are your cold private key plus both public keys. To reveal the device private key, use Settings → Reveal Device Key."
+ },
+ "hotKeyRevealWarningTitle": {
+ "message": "Bevor Sie Ihren Geräteschlüssel preisgeben",
+ "englishSource": "Before you reveal your device key"
+ },
+ "hotKeyRevealWarningBody": {
+ "message": "Jeder mit diesem Schlüssel kann Transaktionen signieren, bis Sie ihn rotieren. Wenn der Schlüssel durchgesickert ist, drehen Sie ihn über Einstellungen → Geräteschlüssel drehen.",
+ "englishSource": "Anyone with this key can sign transactions until you rotate it. If the key is leaked, rotate it via Settings → Rotate Device Key."
+ },
"invalidUrl": {
"message": "Geben Sie eine gültige URL ein, die mit http:// oder https:// beginnt.",
"englishSource": "Enter a valid URL starting with http:// or https://"
diff --git a/public/_locales/en/en.json b/public/_locales/en/en.json
index f367bcdac..98d494c73 100644
--- a/public/_locales/en/en.json
+++ b/public/_locales/en/en.json
@@ -341,6 +341,7 @@
"minimumCharsWithAtLeast": "Minimum 8 characters with at least 1 number",
"backUpWalletInstructions": "Save these 12 words and store in a secure place.",
"doNotShareWithAnywone": "Do not share with anyone.",
+ "seedPhraseRecoveryCaption": "Your seed phrase is your recovery key — used to rotate device keys, not for everyday transactions.",
"select": "Select",
"selected": "Selected",
"additionalDownloads": "Additional Downloads",
@@ -382,9 +383,29 @@
"newGuardianEndpoint": "New Guardian Endpoint",
"switchGuardian": "Switch Guardian",
"switchGuardianDescription": "Point your Guardian account at a new guardian endpoint. The switch is signed as an on-chain proposal.",
+ "switchGuardianConfirmation": "This is signed with your recovery (cold) key and co-signed by your current guardian. Your funds and account stay the same. Continue?",
+ "confirmSwitchGuardian": "Confirm switch",
"switchingGuardian": "Switching guardian...",
"guardianSwitched": "Guardian switched successfully.",
"guardianEndpointUnchanged": "That's already your current guardian.",
+ "replaceHotKey": "Rotate device key",
+ "replaceHotKeyDescription": "Generate a new device (hot) key and replace the current one on-chain. Useful if you suspect your device is compromised.",
+ "replaceHotKeyConfirmation": "This is signed with your recovery (cold) key. Your seed phrase is not required, and your funds and account stay the same. Continue?",
+ "confirmReplaceHotKey": "Confirm rotation",
+ "hotKeyRotated": "Device key rotated successfully.",
+ "activateHotKeyBannerTitle": "Activate device key",
+ "activateHotKeyBannerBody": "Generate a device key for this recovered account. Required before sending.",
+ "activateHotKeyBannerCta": "Activate",
+ "activateHotKeyBannerError": "Failed to activate device key. Try again.",
+ "revealHotKey": "Reveal device key",
+ "revealHotKeyDescription": "Your device (hot) key is rotatable from Settings → Rotate Device Key, so revealing it is lower-stakes than your seed phrase — but anyone who holds it can sign transactions until you rotate.",
+ "hotPrivateKey": "Device Private Key",
+ "hotPublicKeyLabel": "Device Public Key",
+ "coldPrivateKey": "Recovery (Cold) Private Key",
+ "coldPublicKeyLabel": "Recovery (Cold) Public Key",
+ "guardianKeysRevealDescription": "Your Guardian account is protected by three keys: a device (hot) key for routine signing, a recovery (cold) key derived from your seed phrase for account-management operations, and an external guardian co-signer. Below are your cold private key plus both public keys. To reveal the device private key, use Settings → Reveal Device Key.",
+ "hotKeyRevealWarningTitle": "Before you reveal your device key",
+ "hotKeyRevealWarningBody": "Anyone with this key can sign transactions until you rotate it. If the key is leaked, rotate it via Settings → Rotate Device Key.",
"invalidUrl": "Enter a valid URL starting with http:// or https://",
"encryptedWalletFileDescription": "Enter your password to access your Encrypted Wallet File.",
"encryptedWalletFileDescriptionHardware": "Unlock with your passcode to access your Encrypted Wallet File.",
diff --git a/public/_locales/en/messages.json b/public/_locales/en/messages.json
index 7f862b516..5bb7604b1 100644
--- a/public/_locales/en/messages.json
+++ b/public/_locales/en/messages.json
@@ -1477,6 +1477,10 @@
"message": "Do not share with anyone.",
"englishSource": "Do not share with anyone."
},
+ "seedPhraseRecoveryCaption": {
+ "message": "Your seed phrase is your recovery key — used to rotate device keys, not for everyday transactions.",
+ "englishSource": "Your seed phrase is your recovery key — used to rotate device keys, not for everyday transactions."
+ },
"select": {
"message": "Select",
"englishSource": "Select"
@@ -1641,6 +1645,14 @@
"message": "Point your Guardian account at a new guardian endpoint. The switch is signed as an on-chain proposal.",
"englishSource": "Point your Guardian account at a new guardian endpoint. The switch is signed as an on-chain proposal."
},
+ "switchGuardianConfirmation": {
+ "message": "This is signed with your recovery (cold) key and co-signed by your current guardian. Your funds and account stay the same. Continue?",
+ "englishSource": "This is signed with your recovery (cold) key and co-signed by your current guardian. Your funds and account stay the same. Continue?"
+ },
+ "confirmSwitchGuardian": {
+ "message": "Confirm switch",
+ "englishSource": "Confirm switch"
+ },
"switchingGuardian": {
"message": "Switching guardian...",
"englishSource": "Switching guardian..."
@@ -1653,6 +1665,78 @@
"message": "That's already your current guardian.",
"englishSource": "That's already your current guardian."
},
+ "replaceHotKey": {
+ "message": "Rotate device key",
+ "englishSource": "Rotate device key"
+ },
+ "replaceHotKeyDescription": {
+ "message": "Generate a new device (hot) key and replace the current one on-chain. Useful if you suspect your device is compromised.",
+ "englishSource": "Generate a new device (hot) key and replace the current one on-chain. Useful if you suspect your device is compromised."
+ },
+ "replaceHotKeyConfirmation": {
+ "message": "This is signed with your recovery (cold) key. Your seed phrase is not required, and your funds and account stay the same. Continue?",
+ "englishSource": "This is signed with your recovery (cold) key. Your seed phrase is not required, and your funds and account stay the same. Continue?"
+ },
+ "confirmReplaceHotKey": {
+ "message": "Confirm rotation",
+ "englishSource": "Confirm rotation"
+ },
+ "hotKeyRotated": {
+ "message": "Device key rotated successfully.",
+ "englishSource": "Device key rotated successfully."
+ },
+ "activateHotKeyBannerTitle": {
+ "message": "Activate device key",
+ "englishSource": "Activate device key"
+ },
+ "activateHotKeyBannerBody": {
+ "message": "Generate a device key for this recovered account. Required before sending.",
+ "englishSource": "Generate a device key for this recovered account. Required before sending."
+ },
+ "activateHotKeyBannerCta": {
+ "message": "Activate",
+ "englishSource": "Activate"
+ },
+ "activateHotKeyBannerError": {
+ "message": "Failed to activate device key. Try again.",
+ "englishSource": "Failed to activate device key. Try again."
+ },
+ "revealHotKey": {
+ "message": "Reveal device key",
+ "englishSource": "Reveal device key"
+ },
+ "revealHotKeyDescription": {
+ "message": "Your device (hot) key is rotatable from Settings → Rotate Device Key, so revealing it is lower-stakes than your seed phrase — but anyone who holds it can sign transactions until you rotate.",
+ "englishSource": "Your device (hot) key is rotatable from Settings → Rotate Device Key, so revealing it is lower-stakes than your seed phrase — but anyone who holds it can sign transactions until you rotate."
+ },
+ "hotPrivateKey": {
+ "message": "Device Private Key",
+ "englishSource": "Device Private Key"
+ },
+ "hotPublicKeyLabel": {
+ "message": "Device Public Key",
+ "englishSource": "Device Public Key"
+ },
+ "coldPrivateKey": {
+ "message": "Recovery (Cold) Private Key",
+ "englishSource": "Recovery (Cold) Private Key"
+ },
+ "coldPublicKeyLabel": {
+ "message": "Recovery (Cold) Public Key",
+ "englishSource": "Recovery (Cold) Public Key"
+ },
+ "guardianKeysRevealDescription": {
+ "message": "Your Guardian account is protected by three keys: a device (hot) key for routine signing, a recovery (cold) key derived from your seed phrase for account-management operations, and an external guardian co-signer. Below are your cold private key plus both public keys. To reveal the device private key, use Settings → Reveal Device Key.",
+ "englishSource": "Your Guardian account is protected by three keys: a device (hot) key for routine signing, a recovery (cold) key derived from your seed phrase for account-management operations, and an external guardian co-signer. Below are your cold private key plus both public keys. To reveal the device private key, use Settings → Reveal Device Key."
+ },
+ "hotKeyRevealWarningTitle": {
+ "message": "Before you reveal your device key",
+ "englishSource": "Before you reveal your device key"
+ },
+ "hotKeyRevealWarningBody": {
+ "message": "Anyone with this key can sign transactions until you rotate it. If the key is leaked, rotate it via Settings → Rotate Device Key.",
+ "englishSource": "Anyone with this key can sign transactions until you rotate it. If the key is leaked, rotate it via Settings → Rotate Device Key."
+ },
"invalidUrl": {
"message": "Enter a valid URL starting with http:// or https://",
"englishSource": "Enter a valid URL starting with http:// or https://"
diff --git a/public/_locales/en_GB/messages.json b/public/_locales/en_GB/messages.json
index 1339f2a79..af8c8130f 100644
--- a/public/_locales/en_GB/messages.json
+++ b/public/_locales/en_GB/messages.json
@@ -1517,6 +1517,10 @@
"message": "Do not share with anyone.",
"englishSource": "Do not share with anyone."
},
+ "seedPhraseRecoveryCaption": {
+ "message": "Your seed phrase is your recovery key — used to rotate device keys, not for everyday transactions.",
+ "englishSource": "Your seed phrase is your recovery key — used to rotate device keys, not for everyday transactions."
+ },
"select": {
"message": "Select",
"note": "Onboarding",
@@ -1700,6 +1704,14 @@
"message": "Point your Guardian account at a new guardian endpoint. The switch is signed as an on-chain proposal.",
"englishSource": "Point your Guardian account at a new guardian endpoint. The switch is signed as an on-chain proposal."
},
+ "switchGuardianConfirmation": {
+ "message": "This is signed with your recovery (cold) key and co-signed by your current guardian. Your funds and account stay the same. Continue?",
+ "englishSource": "This is signed with your recovery (cold) key and co-signed by your current guardian. Your funds and account stay the same. Continue?"
+ },
+ "confirmSwitchGuardian": {
+ "message": "Confirm switch",
+ "englishSource": "Confirm switch"
+ },
"switchingGuardian": {
"message": "Switching guardian...",
"englishSource": "Switching guardian..."
@@ -1712,6 +1724,78 @@
"message": "That's already your current guardian.",
"englishSource": "That's already your current guardian."
},
+ "replaceHotKey": {
+ "message": "Rotate device key",
+ "englishSource": "Rotate device key"
+ },
+ "replaceHotKeyDescription": {
+ "message": "Generate a new device (hot) key and replace the current one on-chain. Useful if you suspect your device is compromised.",
+ "englishSource": "Generate a new device (hot) key and replace the current one on-chain. Useful if you suspect your device is compromised."
+ },
+ "replaceHotKeyConfirmation": {
+ "message": "This is signed with your recovery (cold) key. Your seed phrase is not required, and your funds and account stay the same. Continue?",
+ "englishSource": "This is signed with your recovery (cold) key. Your seed phrase is not required, and your funds and account stay the same. Continue?"
+ },
+ "confirmReplaceHotKey": {
+ "message": "Confirm rotation",
+ "englishSource": "Confirm rotation"
+ },
+ "hotKeyRotated": {
+ "message": "Device key rotated successfully.",
+ "englishSource": "Device key rotated successfully."
+ },
+ "activateHotKeyBannerTitle": {
+ "message": "Activate device key",
+ "englishSource": "Activate device key"
+ },
+ "activateHotKeyBannerBody": {
+ "message": "Generate a device key for this recovered account. Required before sending.",
+ "englishSource": "Generate a device key for this recovered account. Required before sending."
+ },
+ "activateHotKeyBannerCta": {
+ "message": "Activate",
+ "englishSource": "Activate"
+ },
+ "activateHotKeyBannerError": {
+ "message": "Failed to activate device key. Try again.",
+ "englishSource": "Failed to activate device key. Try again."
+ },
+ "revealHotKey": {
+ "message": "Reveal device key",
+ "englishSource": "Reveal device key"
+ },
+ "revealHotKeyDescription": {
+ "message": "Your device (hot) key is rotatable from Settings → Rotate Device Key, so revealing it is lower-stakes than your seed phrase — but anyone who holds it can sign transactions until you rotate.",
+ "englishSource": "Your device (hot) key is rotatable from Settings → Rotate Device Key, so revealing it is lower-stakes than your seed phrase — but anyone who holds it can sign transactions until you rotate."
+ },
+ "hotPrivateKey": {
+ "message": "Device Private Key",
+ "englishSource": "Device Private Key"
+ },
+ "hotPublicKeyLabel": {
+ "message": "Device Public Key",
+ "englishSource": "Device Public Key"
+ },
+ "coldPrivateKey": {
+ "message": "Recovery (Cold) Private Key",
+ "englishSource": "Recovery (Cold) Private Key"
+ },
+ "coldPublicKeyLabel": {
+ "message": "Recovery (Cold) Public Key",
+ "englishSource": "Recovery (Cold) Public Key"
+ },
+ "guardianKeysRevealDescription": {
+ "message": "Your Guardian account is protected by three keys: a device (hot) key for routine signing, a recovery (cold) key derived from your seed phrase for account-management operations, and an external guardian co-signer. Below are your cold private key plus both public keys. To reveal the device private key, use Settings → Reveal Device Key.",
+ "englishSource": "Your Guardian account is protected by three keys: a device (hot) key for routine signing, a recovery (cold) key derived from your seed phrase for account-management operations, and an external guardian co-signer. Below are your cold private key plus both public keys. To reveal the device private key, use Settings → Reveal Device Key."
+ },
+ "hotKeyRevealWarningTitle": {
+ "message": "Before you reveal your device key",
+ "englishSource": "Before you reveal your device key"
+ },
+ "hotKeyRevealWarningBody": {
+ "message": "Anyone with this key can sign transactions until you rotate it. If the key is leaked, rotate it via Settings → Rotate Device Key.",
+ "englishSource": "Anyone with this key can sign transactions until you rotate it. If the key is leaked, rotate it via Settings → Rotate Device Key."
+ },
"invalidUrl": {
"message": "Enter a valid URL starting with http:// or https://",
"englishSource": "Enter a valid URL starting with http:// or https://"
diff --git a/public/_locales/es/messages.json b/public/_locales/es/messages.json
index 6b974f235..6aa0d7da4 100644
--- a/public/_locales/es/messages.json
+++ b/public/_locales/es/messages.json
@@ -1450,6 +1450,10 @@
"message": "No compartir con nadie.",
"englishSource": "Do not share with anyone."
},
+ "seedPhraseRecoveryCaption": {
+ "message": "Su seed phrase es su clave de recuperación: se utiliza para rotar las claves del dispositivo, no para las transacciones diarias.",
+ "englishSource": "Your seed phrase is your recovery key — used to rotate device keys, not for everyday transactions."
+ },
"select": {
"message": "Seleccionar",
"englishSource": "Select"
@@ -1614,6 +1618,14 @@
"message": "Apunte su cuenta de Guardian a un nuevo punto final de Guardian. El cambio está firmado como una propuesta en cadena.",
"englishSource": "Point your Guardian account at a new guardian endpoint. The switch is signed as an on-chain proposal."
},
+ "switchGuardianConfirmation": {
+ "message": "Esto está firmado con su clave de recuperación (fría) y firmado conjuntamente por su tutor actual. Sus fondos y su cuenta permanecen igual. ¿Continuar?",
+ "englishSource": "This is signed with your recovery (cold) key and co-signed by your current guardian. Your funds and account stay the same. Continue?"
+ },
+ "confirmSwitchGuardian": {
+ "message": "Confirmar cambio",
+ "englishSource": "Confirm switch"
+ },
"switchingGuardian": {
"message": "Cambiando de guardián...",
"englishSource": "Switching guardian..."
@@ -1626,6 +1638,78 @@
"message": "Ese ya es tu tutor actual.",
"englishSource": "That's already your current guardian."
},
+ "replaceHotKey": {
+ "message": "Girar la tecla del dispositivo",
+ "englishSource": "Rotate device key"
+ },
+ "replaceHotKeyDescription": {
+ "message": "Genere una nueva clave (de acceso rápido) de dispositivo y reemplace la actual en la cadena. Útil si sospecha que su dispositivo está comprometido.",
+ "englishSource": "Generate a new device (hot) key and replace the current one on-chain. Useful if you suspect your device is compromised."
+ },
+ "replaceHotKeyConfirmation": {
+ "message": "Esto está firmado con su clave de recuperación (fría). Su seed phrase no es obligatorio y sus fondos y su cuenta siguen siendo los mismos. ¿Continuar?",
+ "englishSource": "This is signed with your recovery (cold) key. Your seed phrase is not required, and your funds and account stay the same. Continue?"
+ },
+ "confirmReplaceHotKey": {
+ "message": "Confirmar rotación",
+ "englishSource": "Confirm rotation"
+ },
+ "hotKeyRotated": {
+ "message": "La clave del dispositivo se giró correctamente.",
+ "englishSource": "Device key rotated successfully."
+ },
+ "activateHotKeyBannerTitle": {
+ "message": "Activar clave del dispositivo",
+ "englishSource": "Activate device key"
+ },
+ "activateHotKeyBannerBody": {
+ "message": "Genere una clave de dispositivo para esta cuenta recuperada. Requerido antes de enviar.",
+ "englishSource": "Generate a device key for this recovered account. Required before sending."
+ },
+ "activateHotKeyBannerCta": {
+ "message": "Activar",
+ "englishSource": "Activate"
+ },
+ "activateHotKeyBannerError": {
+ "message": "No se pudo activar la clave del dispositivo. Intentar otra vez.",
+ "englishSource": "Failed to activate device key. Try again."
+ },
+ "revealHotKey": {
+ "message": "Revelar clave del dispositivo",
+ "englishSource": "Reveal device key"
+ },
+ "revealHotKeyDescription": {
+ "message": "La tecla de acceso rápido de su dispositivo se puede girar desde Configuración → Rotar clave de dispositivo, por lo que revelarla es menos arriesgado que su seed phrase, pero cualquiera que la tenga puede firmar transacciones hasta que la rote.",
+ "englishSource": "Your device (hot) key is rotatable from Settings → Rotate Device Key, so revealing it is lower-stakes than your seed phrase — but anyone who holds it can sign transactions until you rotate."
+ },
+ "hotPrivateKey": {
+ "message": "Clave privada del dispositivo",
+ "englishSource": "Device Private Key"
+ },
+ "hotPublicKeyLabel": {
+ "message": "Clave pública del dispositivo",
+ "englishSource": "Device Public Key"
+ },
+ "coldPrivateKey": {
+ "message": "Clave privada de recuperación (en frío)",
+ "englishSource": "Recovery (Cold) Private Key"
+ },
+ "coldPublicKeyLabel": {
+ "message": "Clave pública de recuperación (en frío)",
+ "englishSource": "Recovery (Cold) Public Key"
+ },
+ "guardianKeysRevealDescription": {
+ "message": "Su cuenta de Guardian está protegida por tres claves: una clave de dispositivo (activa) para la firma de rutina, una clave de recuperación (fría) derivada de su seed phrase para operaciones de administración de cuentas y un cofirmante externo de Guardian. A continuación se muestra su clave privada fría más ambas claves públicas. Para revelar la clave privada del dispositivo, use Configuración → Revelar clave del dispositivo.",
+ "englishSource": "Your Guardian account is protected by three keys: a device (hot) key for routine signing, a recovery (cold) key derived from your seed phrase for account-management operations, and an external guardian co-signer. Below are your cold private key plus both public keys. To reveal the device private key, use Settings → Reveal Device Key."
+ },
+ "hotKeyRevealWarningTitle": {
+ "message": "Antes de revelar la clave de tu dispositivo",
+ "englishSource": "Before you reveal your device key"
+ },
+ "hotKeyRevealWarningBody": {
+ "message": "Cualquier persona con esta clave puede firmar transacciones hasta que la rotes. Si se filtra la clave, gírela a través de Configuración → Girar clave del dispositivo.",
+ "englishSource": "Anyone with this key can sign transactions until you rotate it. If the key is leaked, rotate it via Settings → Rotate Device Key."
+ },
"invalidUrl": {
"message": "Introduzca una URL válida que comience con http:// o https://",
"englishSource": "Enter a valid URL starting with http:// or https://"
diff --git a/public/_locales/fr/messages.json b/public/_locales/fr/messages.json
index 7b74594f6..df4a61bd3 100644
--- a/public/_locales/fr/messages.json
+++ b/public/_locales/fr/messages.json
@@ -1490,6 +1490,10 @@
"message": "Ne partagez avec personne.",
"englishSource": "Do not share with anyone."
},
+ "seedPhraseRecoveryCaption": {
+ "message": "Votre seed phrase est votre clé de récupération : elle est utilisée pour alterner les clés de l'appareil, et non pour les transactions quotidiennes.",
+ "englishSource": "Your seed phrase is your recovery key — used to rotate device keys, not for everyday transactions."
+ },
"select": {
"message": "Sélectionner",
"note": "Onboarding",
@@ -1671,6 +1675,14 @@
"message": "Pointez votre compte Guardian vers un nouveau point de terminaison Guardian. Le changement est signé comme une proposition en chaîne.",
"englishSource": "Point your Guardian account at a new guardian endpoint. The switch is signed as an on-chain proposal."
},
+ "switchGuardianConfirmation": {
+ "message": "Celui-ci est signé avec votre clé de récupération (à froid) et co-signé par votre tuteur actuel. Vos fonds et votre compte restent les mêmes. Continuer?",
+ "englishSource": "This is signed with your recovery (cold) key and co-signed by your current guardian. Your funds and account stay the same. Continue?"
+ },
+ "confirmSwitchGuardian": {
+ "message": "Confirmer le changement",
+ "englishSource": "Confirm switch"
+ },
"switchingGuardian": {
"message": "Changer de tuteur...",
"englishSource": "Switching guardian..."
@@ -1683,6 +1695,78 @@
"message": "C'est déjà votre tuteur actuel.",
"englishSource": "That's already your current guardian."
},
+ "replaceHotKey": {
+ "message": "Faire pivoter la clé de l'appareil",
+ "englishSource": "Rotate device key"
+ },
+ "replaceHotKeyDescription": {
+ "message": "Générez une nouvelle touche (hot) de périphérique et remplacez celle actuelle sur la chaîne. Utile si vous pensez que votre appareil est compromis.",
+ "englishSource": "Generate a new device (hot) key and replace the current one on-chain. Useful if you suspect your device is compromised."
+ },
+ "replaceHotKeyConfirmation": {
+ "message": "Ceci est signé avec votre clé de récupération (à froid). Votre seed phrase n'est pas requis, et vos fonds et votre compte restent les mêmes. Continuer?",
+ "englishSource": "This is signed with your recovery (cold) key. Your seed phrase is not required, and your funds and account stay the same. Continue?"
+ },
+ "confirmReplaceHotKey": {
+ "message": "Confirmer la rotation",
+ "englishSource": "Confirm rotation"
+ },
+ "hotKeyRotated": {
+ "message": "La clé de l'appareil a été pivotée avec succès.",
+ "englishSource": "Device key rotated successfully."
+ },
+ "activateHotKeyBannerTitle": {
+ "message": "Activer la clé de l'appareil",
+ "englishSource": "Activate device key"
+ },
+ "activateHotKeyBannerBody": {
+ "message": "Générez une clé de périphérique pour ce compte récupéré. Obligatoire avant l'envoi.",
+ "englishSource": "Generate a device key for this recovered account. Required before sending."
+ },
+ "activateHotKeyBannerCta": {
+ "message": "Activer",
+ "englishSource": "Activate"
+ },
+ "activateHotKeyBannerError": {
+ "message": "Échec de l'activation de la clé de l'appareil. Essayer à nouveau.",
+ "englishSource": "Failed to activate device key. Try again."
+ },
+ "revealHotKey": {
+ "message": "Révéler la clé de l'appareil",
+ "englishSource": "Reveal device key"
+ },
+ "revealHotKeyDescription": {
+ "message": "La touche (raccourcie) de votre appareil peut être tournée depuis Paramètres → Rotation de la clé de l'appareil, ce qui révèle que ses enjeux sont inférieurs à ceux de votre seed phrase — mais toute personne qui la détient peut signer des transactions jusqu'à ce que vous effectuiez la rotation.",
+ "englishSource": "Your device (hot) key is rotatable from Settings → Rotate Device Key, so revealing it is lower-stakes than your seed phrase — but anyone who holds it can sign transactions until you rotate."
+ },
+ "hotPrivateKey": {
+ "message": "Clé privée de l'appareil",
+ "englishSource": "Device Private Key"
+ },
+ "hotPublicKeyLabel": {
+ "message": "Clé publique de l'appareil",
+ "englishSource": "Device Public Key"
+ },
+ "coldPrivateKey": {
+ "message": "Clé privée de récupération (à froid)",
+ "englishSource": "Recovery (Cold) Private Key"
+ },
+ "coldPublicKeyLabel": {
+ "message": "Clé publique de récupération (à froid)",
+ "englishSource": "Recovery (Cold) Public Key"
+ },
+ "guardianKeysRevealDescription": {
+ "message": "Votre compte Guardian est protégé par trois clés : une touche de périphérique (raccourcie) pour la signature de routine, une clé de récupération (à froid) dérivée de votre seed phrase pour les opérations de gestion de compte et un cosignataire tuteur externe. Vous trouverez ci-dessous votre clé privée froide ainsi que les deux clés publiques. Pour révéler la clé privée de l'appareil, utilisez Paramètres → Révéler la clé de l'appareil.",
+ "englishSource": "Your Guardian account is protected by three keys: a device (hot) key for routine signing, a recovery (cold) key derived from your seed phrase for account-management operations, and an external guardian co-signer. Below are your cold private key plus both public keys. To reveal the device private key, use Settings → Reveal Device Key."
+ },
+ "hotKeyRevealWarningTitle": {
+ "message": "Avant de révéler la clé de votre appareil",
+ "englishSource": "Before you reveal your device key"
+ },
+ "hotKeyRevealWarningBody": {
+ "message": "Toute personne possédant cette clé peut signer des transactions jusqu'à ce que vous la tourniez. Si la clé fuit, faites-la pivoter via Paramètres → Rotation de la clé de l'appareil.",
+ "englishSource": "Anyone with this key can sign transactions until you rotate it. If the key is leaked, rotate it via Settings → Rotate Device Key."
+ },
"invalidUrl": {
"message": "Saisissez une URL valide commençant par http:// ou https://",
"englishSource": "Enter a valid URL starting with http:// or https://"
diff --git a/public/_locales/ja/messages.json b/public/_locales/ja/messages.json
index 59b3ae81b..905b6b785 100644
--- a/public/_locales/ja/messages.json
+++ b/public/_locales/ja/messages.json
@@ -1490,6 +1490,10 @@
"message": "誰とも共有しないでください。",
"englishSource": "Do not share with anyone."
},
+ "seedPhraseRecoveryCaption": {
+ "message": "seed phrase は回復キーです。日常のトランザクションではなく、デバイス キーをローテーションするために使用されます。",
+ "englishSource": "Your seed phrase is your recovery key — used to rotate device keys, not for everyday transactions."
+ },
"select": {
"message": "選択する",
"note": "Onboarding",
@@ -1672,6 +1676,14 @@
"message": "Guardian アカウントを新しいガーディアン エンドポイントに向けます。スイッチはオンチェーン プロポーザルとして署名されます。",
"englishSource": "Point your Guardian account at a new guardian endpoint. The switch is signed as an on-chain proposal."
},
+ "switchGuardianConfirmation": {
+ "message": "これは回復 (コールド) キーで署名され、現在の保護者によって共同署名されています。資金とアカウントはそのまま残ります。続く?",
+ "englishSource": "This is signed with your recovery (cold) key and co-signed by your current guardian. Your funds and account stay the same. Continue?"
+ },
+ "confirmSwitchGuardian": {
+ "message": "スイッチの確認",
+ "englishSource": "Confirm switch"
+ },
"switchingGuardian": {
"message": "守護者を切り替えます...",
"englishSource": "Switching guardian..."
@@ -1684,6 +1696,78 @@
"message": "それはすでにあなたの現在の保護者です。",
"englishSource": "That's already your current guardian."
},
+ "replaceHotKey": {
+ "message": "デバイスキーをローテーションする",
+ "englishSource": "Rotate device key"
+ },
+ "replaceHotKeyDescription": {
+ "message": "新しいデバイス (ホット) キーを生成し、オンチェーン上の現在のキーを置き換えます。デバイスが侵害されている疑いがある場合に役立ちます。",
+ "englishSource": "Generate a new device (hot) key and replace the current one on-chain. Useful if you suspect your device is compromised."
+ },
+ "replaceHotKeyConfirmation": {
+ "message": "これは回復 (コールド) キーで署名されています。 seed phrase は必要ありません。資金とアカウントは変わりません。続く?",
+ "englishSource": "This is signed with your recovery (cold) key. Your seed phrase is not required, and your funds and account stay the same. Continue?"
+ },
+ "confirmReplaceHotKey": {
+ "message": "回転確認",
+ "englishSource": "Confirm rotation"
+ },
+ "hotKeyRotated": {
+ "message": "デバイスキーが正常にローテーションされました。",
+ "englishSource": "Device key rotated successfully."
+ },
+ "activateHotKeyBannerTitle": {
+ "message": "デバイスキーをアクティブ化する",
+ "englishSource": "Activate device key"
+ },
+ "activateHotKeyBannerBody": {
+ "message": "この回復されたアカウントのデバイス キーを生成します。送信する前に必須です。",
+ "englishSource": "Generate a device key for this recovered account. Required before sending."
+ },
+ "activateHotKeyBannerCta": {
+ "message": "活性化",
+ "englishSource": "Activate"
+ },
+ "activateHotKeyBannerError": {
+ "message": "デバイスキーの有効化に失敗しました。もう一度やり直してください。",
+ "englishSource": "Failed to activate device key. Try again."
+ },
+ "revealHotKey": {
+ "message": "デバイスキーを明らかにする",
+ "englishSource": "Reveal device key"
+ },
+ "revealHotKeyDescription": {
+ "message": "デバイス (ホット) キーは [設定] → [デバイス キーの回転] で回転できるため、seed phrase よりも公開するリスクは低くなります。ただし、デバイス キーを保持している人は誰でも、あなたが回転するまでトランザクションに署名できます。",
+ "englishSource": "Your device (hot) key is rotatable from Settings → Rotate Device Key, so revealing it is lower-stakes than your seed phrase — but anyone who holds it can sign transactions until you rotate."
+ },
+ "hotPrivateKey": {
+ "message": "デバイスの秘密鍵",
+ "englishSource": "Device Private Key"
+ },
+ "hotPublicKeyLabel": {
+ "message": "デバイスの公開鍵",
+ "englishSource": "Device Public Key"
+ },
+ "coldPrivateKey": {
+ "message": "リカバリ (コールド) 秘密キー",
+ "englishSource": "Recovery (Cold) Private Key"
+ },
+ "coldPublicKeyLabel": {
+ "message": "リカバリ (コールド) 公開キー",
+ "englishSource": "Recovery (Cold) Public Key"
+ },
+ "guardianKeysRevealDescription": {
+ "message": "Guardian アカウントは、日常的な署名用のデバイス (ホット) キー、アカウント管理操作用の seed phrase から派生した回復 (コールド) キー、および外部ガーディアン共同署名者の 3 つのキーによって保護されています。以下は、コールド秘密キーと両方の公開キーです。デバイスの秘密キーを公開するには、[設定] → [デバイス キーの公開] を使用します。",
+ "englishSource": "Your Guardian account is protected by three keys: a device (hot) key for routine signing, a recovery (cold) key derived from your seed phrase for account-management operations, and an external guardian co-signer. Below are your cold private key plus both public keys. To reveal the device private key, use Settings → Reveal Device Key."
+ },
+ "hotKeyRevealWarningTitle": {
+ "message": "デバイスキーを公開する前に",
+ "englishSource": "Before you reveal your device key"
+ },
+ "hotKeyRevealWarningBody": {
+ "message": "このキーを持っている人は誰でも、キーをローテーションするまでトランザクションに署名できます。キーが漏洩した場合は、「設定」→「デバイス キーのローテーション」でキーをローテーションします。",
+ "englishSource": "Anyone with this key can sign transactions until you rotate it. If the key is leaked, rotate it via Settings → Rotate Device Key."
+ },
"invalidUrl": {
"message": "http:// または https:// で始まる有効な URL を入力してください",
"englishSource": "Enter a valid URL starting with http:// or https://"
diff --git a/public/_locales/ko/messages.json b/public/_locales/ko/messages.json
index 49ad1060e..70aa2bcbb 100644
--- a/public/_locales/ko/messages.json
+++ b/public/_locales/ko/messages.json
@@ -1490,6 +1490,10 @@
"message": "누구와도 공유하지 마십시오.",
"englishSource": "Do not share with anyone."
},
+ "seedPhraseRecoveryCaption": {
+ "message": "seed phrase은 복구 키입니다. 일상적인 거래가 아닌 장치 키를 교체하는 데 사용됩니다.",
+ "englishSource": "Your seed phrase is your recovery key — used to rotate device keys, not for everyday transactions."
+ },
"select": {
"message": "선택",
"note": "Onboarding",
@@ -1672,6 +1676,14 @@
"message": "Guardian 계정을 새로운 Guardian 엔드포인트로 지정하세요. 스위치는 온체인 제안으로 서명됩니다.",
"englishSource": "Point your Guardian account at a new guardian endpoint. The switch is signed as an on-chain proposal."
},
+ "switchGuardianConfirmation": {
+ "message": "이는 복구(콜드) 키로 서명되며 현재 보호자가 공동 서명합니다. 귀하의 자금과 계좌는 동일하게 유지됩니다. 계속하다?",
+ "englishSource": "This is signed with your recovery (cold) key and co-signed by your current guardian. Your funds and account stay the same. Continue?"
+ },
+ "confirmSwitchGuardian": {
+ "message": "스위치 확인",
+ "englishSource": "Confirm switch"
+ },
"switchingGuardian": {
"message": "보호자를 바꾸는 중...",
"englishSource": "Switching guardian..."
@@ -1684,6 +1696,78 @@
"message": "그 사람은 이미 현재 보호자입니다.",
"englishSource": "That's already your current guardian."
},
+ "replaceHotKey": {
+ "message": "장치 키 회전",
+ "englishSource": "Rotate device key"
+ },
+ "replaceHotKeyDescription": {
+ "message": "새 장치(핫) 키를 생성하고 현재 온체인 키를 교체합니다. 장치가 손상된 것으로 의심되는 경우 유용합니다.",
+ "englishSource": "Generate a new device (hot) key and replace the current one on-chain. Useful if you suspect your device is compromised."
+ },
+ "replaceHotKeyConfirmation": {
+ "message": "이는 복구(콜드) 키로 서명됩니다. 귀하의 seed phrase은 필요하지 않으며 귀하의 자금과 계좌는 동일하게 유지됩니다. 계속하다?",
+ "englishSource": "This is signed with your recovery (cold) key. Your seed phrase is not required, and your funds and account stay the same. Continue?"
+ },
+ "confirmReplaceHotKey": {
+ "message": "순환 확인",
+ "englishSource": "Confirm rotation"
+ },
+ "hotKeyRotated": {
+ "message": "장치 키가 성공적으로 순환되었습니다.",
+ "englishSource": "Device key rotated successfully."
+ },
+ "activateHotKeyBannerTitle": {
+ "message": "장치 키 활성화",
+ "englishSource": "Activate device key"
+ },
+ "activateHotKeyBannerBody": {
+ "message": "복구된 계정에 대한 장치 키를 생성하세요. 보내기 전에 필요합니다.",
+ "englishSource": "Generate a device key for this recovered account. Required before sending."
+ },
+ "activateHotKeyBannerCta": {
+ "message": "활성화",
+ "englishSource": "Activate"
+ },
+ "activateHotKeyBannerError": {
+ "message": "장치 키를 활성화하지 못했습니다. 다시 시도해 보세요.",
+ "englishSource": "Failed to activate device key. Try again."
+ },
+ "revealHotKey": {
+ "message": "장치 키 공개",
+ "englishSource": "Reveal device key"
+ },
+ "revealHotKeyDescription": {
+ "message": "귀하의 장치(핫) 키는 설정 → 장치 키 회전에서 회전 가능하므로 이를 공개하는 것은 seed phrase보다 위험이 낮습니다. 하지만 키를 보유하고 있는 사람은 귀하가 회전할 때까지 거래에 서명할 수 있습니다.",
+ "englishSource": "Your device (hot) key is rotatable from Settings → Rotate Device Key, so revealing it is lower-stakes than your seed phrase — but anyone who holds it can sign transactions until you rotate."
+ },
+ "hotPrivateKey": {
+ "message": "장치 개인 키",
+ "englishSource": "Device Private Key"
+ },
+ "hotPublicKeyLabel": {
+ "message": "장치 공개 키",
+ "englishSource": "Device Public Key"
+ },
+ "coldPrivateKey": {
+ "message": "복구(콜드) 개인 키",
+ "englishSource": "Recovery (Cold) Private Key"
+ },
+ "coldPublicKeyLabel": {
+ "message": "복구(콜드) 공개 키",
+ "englishSource": "Recovery (Cold) Public Key"
+ },
+ "guardianKeysRevealDescription": {
+ "message": "귀하의 Guardian 계정은 정기 서명을 위한 장치(핫) 키, 계정 관리 작업을 위한 seed phrase에서 파생된 복구(콜드) 키, 외부 보호자 공동 서명자의 세 가지 키로 보호됩니다. 다음은 콜드 개인 키와 두 공개 키입니다. 장치 개인 키를 공개하려면 설정 → 장치 키 공개를 사용하세요.",
+ "englishSource": "Your Guardian account is protected by three keys: a device (hot) key for routine signing, a recovery (cold) key derived from your seed phrase for account-management operations, and an external guardian co-signer. Below are your cold private key plus both public keys. To reveal the device private key, use Settings → Reveal Device Key."
+ },
+ "hotKeyRevealWarningTitle": {
+ "message": "장치 키를 공개하기 전에",
+ "englishSource": "Before you reveal your device key"
+ },
+ "hotKeyRevealWarningBody": {
+ "message": "이 키를 가진 사람은 누구나 교체할 때까지 거래에 서명할 수 있습니다. 키가 유출된 경우 설정 → 기기 키 회전을 통해 키를 회전하세요.",
+ "englishSource": "Anyone with this key can sign transactions until you rotate it. If the key is leaked, rotate it via Settings → Rotate Device Key."
+ },
"invalidUrl": {
"message": "http:// 또는 https://로 시작하는 유효한 URL을 입력하세요.",
"englishSource": "Enter a valid URL starting with http:// or https://"
diff --git a/public/_locales/pl/messages.json b/public/_locales/pl/messages.json
index c565e40e0..2d0d541e9 100644
--- a/public/_locales/pl/messages.json
+++ b/public/_locales/pl/messages.json
@@ -1450,6 +1450,10 @@
"message": "Nie udostępniaj nikomu.",
"englishSource": "Do not share with anyone."
},
+ "seedPhraseRecoveryCaption": {
+ "message": "Twój seed phrase to klucz odzyskiwania — używany do rotacji kluczy urządzenia, a nie do codziennych transakcji.",
+ "englishSource": "Your seed phrase is your recovery key — used to rotate device keys, not for everyday transactions."
+ },
"select": {
"message": "Wybierać",
"englishSource": "Select"
@@ -1614,6 +1618,14 @@
"message": "Skieruj swoje konto Guardiana na nowy punkt końcowy opiekuna. Przełącznik jest podpisany jako propozycja on-chain.",
"englishSource": "Point your Guardian account at a new guardian endpoint. The switch is signed as an on-chain proposal."
},
+ "switchGuardianConfirmation": {
+ "message": "Jest to podpisane Twoim (zimnym) kluczem odzyskiwania i podpisane przez Twojego obecnego opiekuna. Twoje środki i konto pozostają takie same. Kontynuować?",
+ "englishSource": "This is signed with your recovery (cold) key and co-signed by your current guardian. Your funds and account stay the same. Continue?"
+ },
+ "confirmSwitchGuardian": {
+ "message": "Potwierdź przełącznik",
+ "englishSource": "Confirm switch"
+ },
"switchingGuardian": {
"message": "Zmiana opiekuna...",
"englishSource": "Switching guardian..."
@@ -1626,6 +1638,78 @@
"message": "To już twój obecny opiekun.",
"englishSource": "That's already your current guardian."
},
+ "replaceHotKey": {
+ "message": "Obróć klucz urządzenia",
+ "englishSource": "Rotate device key"
+ },
+ "replaceHotKeyDescription": {
+ "message": "Wygeneruj nowy (gorący) klucz urządzenia i zastąp bieżący w łańcuchu. Przydatne, jeśli podejrzewasz, że Twoje urządzenie zostało naruszone.",
+ "englishSource": "Generate a new device (hot) key and replace the current one on-chain. Useful if you suspect your device is compromised."
+ },
+ "replaceHotKeyConfirmation": {
+ "message": "Jest to podpisane za pomocą klucza odzyskiwania (zimnego). Twój seed phrase nie jest wymagany, a Twoje środki i konto pozostają takie same. Kontynuować?",
+ "englishSource": "This is signed with your recovery (cold) key. Your seed phrase is not required, and your funds and account stay the same. Continue?"
+ },
+ "confirmReplaceHotKey": {
+ "message": "Potwierdź obrót",
+ "englishSource": "Confirm rotation"
+ },
+ "hotKeyRotated": {
+ "message": "Klucz urządzenia został pomyślnie obrócony.",
+ "englishSource": "Device key rotated successfully."
+ },
+ "activateHotKeyBannerTitle": {
+ "message": "Aktywuj klucz urządzenia",
+ "englishSource": "Activate device key"
+ },
+ "activateHotKeyBannerBody": {
+ "message": "Wygeneruj klucz urządzenia dla tego odzyskanego konta. Wymagane przed wysłaniem.",
+ "englishSource": "Generate a device key for this recovered account. Required before sending."
+ },
+ "activateHotKeyBannerCta": {
+ "message": "Aktywować",
+ "englishSource": "Activate"
+ },
+ "activateHotKeyBannerError": {
+ "message": "Nie udało się aktywować klucza urządzenia. Spróbuj ponownie.",
+ "englishSource": "Failed to activate device key. Try again."
+ },
+ "revealHotKey": {
+ "message": "Odkryj klucz urządzenia",
+ "englishSource": "Reveal device key"
+ },
+ "revealHotKeyDescription": {
+ "message": "Twój klucz urządzenia (gorący) można obracać, wybierając Ustawienia → Obróć klucz urządzenia, więc ujawnienie go jest obarczone niższą stawką niż klucz seed phrase — ale każdy, kto go trzyma, może podpisywać transakcje do czasu rotacji.",
+ "englishSource": "Your device (hot) key is rotatable from Settings → Rotate Device Key, so revealing it is lower-stakes than your seed phrase — but anyone who holds it can sign transactions until you rotate."
+ },
+ "hotPrivateKey": {
+ "message": "Klucz prywatny urządzenia",
+ "englishSource": "Device Private Key"
+ },
+ "hotPublicKeyLabel": {
+ "message": "Klucz publiczny urządzenia",
+ "englishSource": "Device Public Key"
+ },
+ "coldPrivateKey": {
+ "message": "Odzyskiwanie (zimnego) klucza prywatnego",
+ "englishSource": "Recovery (Cold) Private Key"
+ },
+ "coldPublicKeyLabel": {
+ "message": "Klucz publiczny odzyskiwania (zimny).",
+ "englishSource": "Recovery (Cold) Public Key"
+ },
+ "guardianKeysRevealDescription": {
+ "message": "Twoje konto Guardian jest chronione trzema kluczami: kluczem urządzenia (gorącym) do rutynowego podpisywania, kluczem odzyskiwania (zimnym) uzyskanym z Twojego seed phrase do operacji zarządzania kontem oraz współsygnatariuszem zewnętrznego opiekuna. Poniżej znajduje się Twój zimny klucz prywatny oraz oba klucze publiczne. Aby ujawnić klucz prywatny urządzenia, użyj opcji Ustawienia → Pokaż klucz urządzenia.",
+ "englishSource": "Your Guardian account is protected by three keys: a device (hot) key for routine signing, a recovery (cold) key derived from your seed phrase for account-management operations, and an external guardian co-signer. Below are your cold private key plus both public keys. To reveal the device private key, use Settings → Reveal Device Key."
+ },
+ "hotKeyRevealWarningTitle": {
+ "message": "Zanim ujawnisz klucz swojego urządzenia",
+ "englishSource": "Before you reveal your device key"
+ },
+ "hotKeyRevealWarningBody": {
+ "message": "Każda osoba posiadająca ten klucz może podpisywać transakcje, dopóki go nie obrócisz. Jeśli klucz wyciekł, obróć go, wybierając Ustawienia → Obróć klucz urządzenia.",
+ "englishSource": "Anyone with this key can sign transactions until you rotate it. If the key is leaked, rotate it via Settings → Rotate Device Key."
+ },
"invalidUrl": {
"message": "Wprowadź prawidłowy adres URL zaczynający się od http:// lub https://",
"englishSource": "Enter a valid URL starting with http:// or https://"
diff --git a/public/_locales/pt/messages.json b/public/_locales/pt/messages.json
index ed8f5b1fc..6d5341848 100644
--- a/public/_locales/pt/messages.json
+++ b/public/_locales/pt/messages.json
@@ -1489,6 +1489,10 @@
"message": "Não compartilhe com ninguém.",
"englishSource": "Do not share with anyone."
},
+ "seedPhraseRecoveryCaption": {
+ "message": "Seu seed phrase é sua chave de recuperação — usada para alternar chaves de dispositivos, não para transações diárias.",
+ "englishSource": "Your seed phrase is your recovery key — used to rotate device keys, not for everyday transactions."
+ },
"select": {
"message": "Selecione",
"note": "Onboarding",
@@ -1670,6 +1674,14 @@
"message": "Aponte sua conta do Guardian para um novo endpoint do Guardian. A mudança é assinada como uma proposta on-chain.",
"englishSource": "Point your Guardian account at a new guardian endpoint. The switch is signed as an on-chain proposal."
},
+ "switchGuardianConfirmation": {
+ "message": "Este é assinado com sua chave de recuperação (fria) e co-assinado pelo seu responsável atual. Seus fundos e conta permanecem os mesmos. Continuar?",
+ "englishSource": "This is signed with your recovery (cold) key and co-signed by your current guardian. Your funds and account stay the same. Continue?"
+ },
+ "confirmSwitchGuardian": {
+ "message": "Confirmar mudança",
+ "englishSource": "Confirm switch"
+ },
"switchingGuardian": {
"message": "Trocando de guardião...",
"englishSource": "Switching guardian..."
@@ -1682,6 +1694,78 @@
"message": "Esse já é seu guardião atual.",
"englishSource": "That's already your current guardian."
},
+ "replaceHotKey": {
+ "message": "Girar tecla do dispositivo",
+ "englishSource": "Rotate device key"
+ },
+ "replaceHotKeyDescription": {
+ "message": "Gere uma nova chave de dispositivo (hot) e substitua a atual na cadeia. Útil se você suspeitar que seu dispositivo está comprometido.",
+ "englishSource": "Generate a new device (hot) key and replace the current one on-chain. Useful if you suspect your device is compromised."
+ },
+ "replaceHotKeyConfirmation": {
+ "message": "Isto é assinado com sua chave de recuperação (fria). Seu seed phrase não é necessário e seus fundos e conta permanecem os mesmos. Continuar?",
+ "englishSource": "This is signed with your recovery (cold) key. Your seed phrase is not required, and your funds and account stay the same. Continue?"
+ },
+ "confirmReplaceHotKey": {
+ "message": "Confirmar rotação",
+ "englishSource": "Confirm rotation"
+ },
+ "hotKeyRotated": {
+ "message": "Chave do dispositivo girada com sucesso.",
+ "englishSource": "Device key rotated successfully."
+ },
+ "activateHotKeyBannerTitle": {
+ "message": "Ativar chave do dispositivo",
+ "englishSource": "Activate device key"
+ },
+ "activateHotKeyBannerBody": {
+ "message": "Gere uma chave de dispositivo para esta conta recuperada. Obrigatório antes do envio.",
+ "englishSource": "Generate a device key for this recovered account. Required before sending."
+ },
+ "activateHotKeyBannerCta": {
+ "message": "Ativar",
+ "englishSource": "Activate"
+ },
+ "activateHotKeyBannerError": {
+ "message": "Falha ao ativar a chave do dispositivo. Tente novamente.",
+ "englishSource": "Failed to activate device key. Try again."
+ },
+ "revealHotKey": {
+ "message": "Revelar chave do dispositivo",
+ "englishSource": "Reveal device key"
+ },
+ "revealHotKeyDescription": {
+ "message": "A tecla (de atalho) do seu dispositivo pode ser girada em Configurações → Girar chave do dispositivo, portanto, revelá-la tem riscos mais baixos do que seu seed phrase - mas qualquer pessoa que a possua pode assinar transações até que você gire.",
+ "englishSource": "Your device (hot) key is rotatable from Settings → Rotate Device Key, so revealing it is lower-stakes than your seed phrase — but anyone who holds it can sign transactions until you rotate."
+ },
+ "hotPrivateKey": {
+ "message": "Chave privada do dispositivo",
+ "englishSource": "Device Private Key"
+ },
+ "hotPublicKeyLabel": {
+ "message": "Chave pública do dispositivo",
+ "englishSource": "Device Public Key"
+ },
+ "coldPrivateKey": {
+ "message": "Chave privada de recuperação (fria)",
+ "englishSource": "Recovery (Cold) Private Key"
+ },
+ "coldPublicKeyLabel": {
+ "message": "Chave pública de recuperação (fria)",
+ "englishSource": "Recovery (Cold) Public Key"
+ },
+ "guardianKeysRevealDescription": {
+ "message": "Sua conta Guardian é protegida por três chaves: uma chave de dispositivo (quente) para assinatura de rotina, uma chave de recuperação (fria) derivada de seu seed phrase para operações de gerenciamento de conta e um fiador guardião externo. Abaixo estão sua chave privada fria e ambas as chaves públicas. Para revelar a chave privada do dispositivo, use Configurações → Revelar chave do dispositivo.",
+ "englishSource": "Your Guardian account is protected by three keys: a device (hot) key for routine signing, a recovery (cold) key derived from your seed phrase for account-management operations, and an external guardian co-signer. Below are your cold private key plus both public keys. To reveal the device private key, use Settings → Reveal Device Key."
+ },
+ "hotKeyRevealWarningTitle": {
+ "message": "Antes de revelar a chave do seu dispositivo",
+ "englishSource": "Before you reveal your device key"
+ },
+ "hotKeyRevealWarningBody": {
+ "message": "Qualquer pessoa com esta chave pode assinar transações até que você a gire. Se a chave vazar, gire-a em Configurações → Girar chave do dispositivo.",
+ "englishSource": "Anyone with this key can sign transactions until you rotate it. If the key is leaked, rotate it via Settings → Rotate Device Key."
+ },
"invalidUrl": {
"message": "Insira um URL válido começando com http:// ou https://",
"englishSource": "Enter a valid URL starting with http:// or https://"
diff --git a/public/_locales/ru/messages.json b/public/_locales/ru/messages.json
index 3079a6070..e16a76b6e 100644
--- a/public/_locales/ru/messages.json
+++ b/public/_locales/ru/messages.json
@@ -1490,6 +1490,10 @@
"message": "Не делитесь ни с кем.",
"englishSource": "Do not share with anyone."
},
+ "seedPhraseRecoveryCaption": {
+ "message": "Ваш seed phrase — это ваш ключ восстановления, который используется для смены ключей устройства, а не для повседневных транзакций.",
+ "englishSource": "Your seed phrase is your recovery key — used to rotate device keys, not for everyday transactions."
+ },
"select": {
"message": "Выбрать",
"note": "Onboarding",
@@ -1673,6 +1677,14 @@
"message": "Направьте свою учетную запись Guardian на новую конечную точку Guardian. Переключатель подписывается как предложение в цепочке.",
"englishSource": "Point your Guardian account at a new guardian endpoint. The switch is signed as an on-chain proposal."
},
+ "switchGuardianConfirmation": {
+ "message": "Он подписан вашим ключом восстановления (холодным) и подписан вашим нынешним опекуном. Ваши средства и счет останутся прежними. Продолжать?",
+ "englishSource": "This is signed with your recovery (cold) key and co-signed by your current guardian. Your funds and account stay the same. Continue?"
+ },
+ "confirmSwitchGuardian": {
+ "message": "Подтвердить переключение",
+ "englishSource": "Confirm switch"
+ },
"switchingGuardian": {
"message": "Смена опекуна...",
"englishSource": "Switching guardian..."
@@ -1685,6 +1697,78 @@
"message": "Это уже ваш нынешний опекун.",
"englishSource": "That's already your current guardian."
},
+ "replaceHotKey": {
+ "message": "Поворот ключа устройства",
+ "englishSource": "Rotate device key"
+ },
+ "replaceHotKeyDescription": {
+ "message": "Создайте новый (горячий) ключ устройства и замените текущий в цепочке. Полезно, если вы подозреваете, что ваше устройство взломано.",
+ "englishSource": "Generate a new device (hot) key and replace the current one on-chain. Useful if you suspect your device is compromised."
+ },
+ "replaceHotKeyConfirmation": {
+ "message": "Он подписан вашим ключом восстановления (холодным). Ваш seed phrase не требуется, а ваши средства и счет останутся прежними. Продолжать?",
+ "englishSource": "This is signed with your recovery (cold) key. Your seed phrase is not required, and your funds and account stay the same. Continue?"
+ },
+ "confirmReplaceHotKey": {
+ "message": "Подтвердить ротацию",
+ "englishSource": "Confirm rotation"
+ },
+ "hotKeyRotated": {
+ "message": "Ключ устройства успешно поменялся.",
+ "englishSource": "Device key rotated successfully."
+ },
+ "activateHotKeyBannerTitle": {
+ "message": "Активировать ключ устройства",
+ "englishSource": "Activate device key"
+ },
+ "activateHotKeyBannerBody": {
+ "message": "Создайте ключ устройства для этой восстановленной учетной записи. Обязательно перед отправкой.",
+ "englishSource": "Generate a device key for this recovered account. Required before sending."
+ },
+ "activateHotKeyBannerCta": {
+ "message": "Активировать",
+ "englishSource": "Activate"
+ },
+ "activateHotKeyBannerError": {
+ "message": "Не удалось активировать ключ устройства. Попробуйте еще раз.",
+ "englishSource": "Failed to activate device key. Try again."
+ },
+ "revealHotKey": {
+ "message": "Показать ключ устройства",
+ "englishSource": "Reveal device key"
+ },
+ "revealHotKeyDescription": {
+ "message": "(Горячую) клавишу вашего устройства можно менять в меню «Настройки» → «Поменять ключ устройства», поэтому ее раскрытие менее важно, чем ваш seed phrase — но любой, кто ее держит, может подписывать транзакции до тех пор, пока вы не поменяете ее.",
+ "englishSource": "Your device (hot) key is rotatable from Settings → Rotate Device Key, so revealing it is lower-stakes than your seed phrase — but anyone who holds it can sign transactions until you rotate."
+ },
+ "hotPrivateKey": {
+ "message": "Закрытый ключ устройства",
+ "englishSource": "Device Private Key"
+ },
+ "hotPublicKeyLabel": {
+ "message": "Открытый ключ устройства",
+ "englishSource": "Device Public Key"
+ },
+ "coldPrivateKey": {
+ "message": "Восстановление (холодного) закрытого ключа",
+ "englishSource": "Recovery (Cold) Private Key"
+ },
+ "coldPublicKeyLabel": {
+ "message": "Открытый ключ восстановления (холодный)",
+ "englishSource": "Recovery (Cold) Public Key"
+ },
+ "guardianKeysRevealDescription": {
+ "message": "Ваша учетная запись Guardian защищена тремя ключами: ключом устройства (горячим) для обычного подписания, ключом восстановления (холодным) ключом, полученным из вашего seed phrase для операций управления учетной записью, и внешним подписавшим лицом-опекуном. Ниже приведены ваш холодный закрытый ключ и оба открытых ключа. Чтобы раскрыть закрытый ключ устройства, выберите «Настройки» → «Показать ключ устройства».",
+ "englishSource": "Your Guardian account is protected by three keys: a device (hot) key for routine signing, a recovery (cold) key derived from your seed phrase for account-management operations, and an external guardian co-signer. Below are your cold private key plus both public keys. To reveal the device private key, use Settings → Reveal Device Key."
+ },
+ "hotKeyRevealWarningTitle": {
+ "message": "Прежде чем раскрыть ключ вашего устройства",
+ "englishSource": "Before you reveal your device key"
+ },
+ "hotKeyRevealWarningBody": {
+ "message": "Любой, у кого есть этот ключ, может подписывать транзакции, пока вы его не поменяете. Если ключ утек, поверните его через «Настройки» → «Повернуть ключ устройства».",
+ "englishSource": "Anyone with this key can sign transactions until you rotate it. If the key is leaked, rotate it via Settings → Rotate Device Key."
+ },
"invalidUrl": {
"message": "Введите действительный URL-адрес, начинающийся с http:// или https://.",
"englishSource": "Enter a valid URL starting with http:// or https://"
diff --git a/public/_locales/tr/messages.json b/public/_locales/tr/messages.json
index 780e8be4a..6f895d5a8 100644
--- a/public/_locales/tr/messages.json
+++ b/public/_locales/tr/messages.json
@@ -1490,6 +1490,10 @@
"message": "Kimseyle paylaşmayın.",
"englishSource": "Do not share with anyone."
},
+ "seedPhraseRecoveryCaption": {
+ "message": "seed phrase kurtarma anahtarınızdır; günlük işlemler için değil, cihaz anahtarlarını döndürmek için kullanılır.",
+ "englishSource": "Your seed phrase is your recovery key — used to rotate device keys, not for everyday transactions."
+ },
"select": {
"message": "Seç",
"noteeee": "Onboarding",
@@ -1672,6 +1676,14 @@
"message": "Guardian hesabınızı yeni bir koruyucu uç noktasına yönlendirin. Anahtar, zincir içi bir teklif olarak imzalandı.",
"englishSource": "Point your Guardian account at a new guardian endpoint. The switch is signed as an on-chain proposal."
},
+ "switchGuardianConfirmation": {
+ "message": "Bu, kurtarma (soğuk) anahtarınızla imzalanır ve mevcut vasiniz tarafından da imzalanır. Paranız ve hesabınız aynı kalır. Devam etmek?",
+ "englishSource": "This is signed with your recovery (cold) key and co-signed by your current guardian. Your funds and account stay the same. Continue?"
+ },
+ "confirmSwitchGuardian": {
+ "message": "Anahtarı onayla",
+ "englishSource": "Confirm switch"
+ },
"switchingGuardian": {
"message": "Vasi değiştiriliyor...",
"englishSource": "Switching guardian..."
@@ -1684,6 +1696,78 @@
"message": "Bu zaten sizin şu anki vasiniz.",
"englishSource": "That's already your current guardian."
},
+ "replaceHotKey": {
+ "message": "Cihaz anahtarını döndür",
+ "englishSource": "Rotate device key"
+ },
+ "replaceHotKeyDescription": {
+ "message": "Yeni bir cihaz (geçiş) anahtarı oluşturun ve zincirdeki mevcut olanı değiştirin. Cihazınızın güvenliğinin ihlal edildiğinden şüpheleniyorsanız kullanışlıdır.",
+ "englishSource": "Generate a new device (hot) key and replace the current one on-chain. Useful if you suspect your device is compromised."
+ },
+ "replaceHotKeyConfirmation": {
+ "message": "Bu, kurtarma (soğuk) anahtarınızla imzalanır. seed phrase kodunuz gerekli değildir ve paranız ve hesabınız aynı kalır. Devam etmek?",
+ "englishSource": "This is signed with your recovery (cold) key. Your seed phrase is not required, and your funds and account stay the same. Continue?"
+ },
+ "confirmReplaceHotKey": {
+ "message": "Döndürmeyi onayla",
+ "englishSource": "Confirm rotation"
+ },
+ "hotKeyRotated": {
+ "message": "Cihaz anahtarı başarıyla döndürüldü.",
+ "englishSource": "Device key rotated successfully."
+ },
+ "activateHotKeyBannerTitle": {
+ "message": "Cihaz anahtarını etkinleştir",
+ "englishSource": "Activate device key"
+ },
+ "activateHotKeyBannerBody": {
+ "message": "Kurtarılan bu hesap için bir cihaz anahtarı oluşturun. Göndermeden önce gereklidir.",
+ "englishSource": "Generate a device key for this recovered account. Required before sending."
+ },
+ "activateHotKeyBannerCta": {
+ "message": "Etkinleştir",
+ "englishSource": "Activate"
+ },
+ "activateHotKeyBannerError": {
+ "message": "Cihaz anahtarı etkinleştirilemedi. Tekrar deneyin.",
+ "englishSource": "Failed to activate device key. Try again."
+ },
+ "revealHotKey": {
+ "message": "Cihaz anahtarını göster",
+ "englishSource": "Reveal device key"
+ },
+ "revealHotKeyDescription": {
+ "message": "Cihaz (geçiş) anahtarınız, Ayarlar → Cihaz Anahtarını Döndür seçeneğinden döndürülebilir; bu nedenle, bunun açığa çıkarılması seed phrase anahtarınızdan daha düşük risklidir; ancak onu tutan herkes, siz rotasyon yapana kadar işlemleri imzalayabilir.",
+ "englishSource": "Your device (hot) key is rotatable from Settings → Rotate Device Key, so revealing it is lower-stakes than your seed phrase — but anyone who holds it can sign transactions until you rotate."
+ },
+ "hotPrivateKey": {
+ "message": "Cihaz Özel Anahtarı",
+ "englishSource": "Device Private Key"
+ },
+ "hotPublicKeyLabel": {
+ "message": "Cihaz Genel Anahtarı",
+ "englishSource": "Device Public Key"
+ },
+ "coldPrivateKey": {
+ "message": "Kurtarma (Soğuk) Özel Anahtar",
+ "englishSource": "Recovery (Cold) Private Key"
+ },
+ "coldPublicKeyLabel": {
+ "message": "Kurtarma (Soğuk) Genel Anahtar",
+ "englishSource": "Recovery (Cold) Public Key"
+ },
+ "guardianKeysRevealDescription": {
+ "message": "Guardian hesabınız üç anahtarla korunur: rutin imzalama için bir cihaz (geçici) anahtar, hesap yönetimi işlemleri için seed phrase hesabınızdan türetilen bir kurtarma (soğuk) anahtar ve harici bir veli ortak imzalayan. Aşağıda soğuk özel anahtarınız ve her iki genel anahtarınız bulunmaktadır. Cihazın özel anahtarını ortaya çıkarmak için Ayarlar → Cihaz Anahtarını Göster seçeneğini kullanın.",
+ "englishSource": "Your Guardian account is protected by three keys: a device (hot) key for routine signing, a recovery (cold) key derived from your seed phrase for account-management operations, and an external guardian co-signer. Below are your cold private key plus both public keys. To reveal the device private key, use Settings → Reveal Device Key."
+ },
+ "hotKeyRevealWarningTitle": {
+ "message": "Cihaz anahtarınızı açıklamadan önce",
+ "englishSource": "Before you reveal your device key"
+ },
+ "hotKeyRevealWarningBody": {
+ "message": "Bu anahtara sahip olan herkes, siz onu değiştirene kadar işlemleri imzalayabilir. Anahtar sızdırılmışsa, Ayarlar → Cihaz Anahtarını Döndür seçeneğini kullanarak onu döndürün.",
+ "englishSource": "Anyone with this key can sign transactions until you rotate it. If the key is leaked, rotate it via Settings → Rotate Device Key."
+ },
"invalidUrl": {
"message": "http:// veya https:// ile başlayan geçerli bir URL girin",
"englishSource": "Enter a valid URL starting with http:// or https://"
diff --git a/public/_locales/uk/messages.json b/public/_locales/uk/messages.json
index 044a16f92..379d00061 100644
--- a/public/_locales/uk/messages.json
+++ b/public/_locales/uk/messages.json
@@ -1490,6 +1490,10 @@
"message": "Ні з ким не діліться.",
"englishSource": "Do not share with anyone."
},
+ "seedPhraseRecoveryCaption": {
+ "message": "Ваш seed phrase є вашим ключем відновлення, який використовується для ротації ключів пристрою, а не для щоденних транзакцій.",
+ "englishSource": "Your seed phrase is your recovery key — used to rotate device keys, not for everyday transactions."
+ },
"select": {
"message": "Вибрати",
"note": "Onboarding",
@@ -1673,6 +1677,14 @@
"message": "Направте свій обліковий запис Guardian на нову кінцеву точку опікуна. Комутатор підписаний як пропозиція в ланцюжку.",
"englishSource": "Point your Guardian account at a new guardian endpoint. The switch is signed as an on-chain proposal."
},
+ "switchGuardianConfirmation": {
+ "message": "Це підписано вашим (холодним) ключем відновлення та підписано вашим поточним опікуном. Ваші кошти та рахунок залишаються без змін. Продовжити?",
+ "englishSource": "This is signed with your recovery (cold) key and co-signed by your current guardian. Your funds and account stay the same. Continue?"
+ },
+ "confirmSwitchGuardian": {
+ "message": "Підтвердьте перемикання",
+ "englishSource": "Confirm switch"
+ },
"switchingGuardian": {
"message": "Зміна опікуна...",
"englishSource": "Switching guardian..."
@@ -1685,6 +1697,78 @@
"message": "Це вже ваш поточний опікун.",
"englishSource": "That's already your current guardian."
},
+ "replaceHotKey": {
+ "message": "Поверніть клавішу пристрою",
+ "englishSource": "Rotate device key"
+ },
+ "replaceHotKeyDescription": {
+ "message": "Згенеруйте новий (гарячий) ключ пристрою та замініть поточний у мережі. Корисно, якщо ви підозрюєте, що ваш пристрій зламано.",
+ "englishSource": "Generate a new device (hot) key and replace the current one on-chain. Useful if you suspect your device is compromised."
+ },
+ "replaceHotKeyConfirmation": {
+ "message": "Це підписано вашим (холодним) ключем відновлення. Ваш seed phrase не потрібен, а ваші кошти та рахунок залишаються незмінними. Продовжити?",
+ "englishSource": "This is signed with your recovery (cold) key. Your seed phrase is not required, and your funds and account stay the same. Continue?"
+ },
+ "confirmReplaceHotKey": {
+ "message": "Підтвердити обертання",
+ "englishSource": "Confirm rotation"
+ },
+ "hotKeyRotated": {
+ "message": "Ключ пристрою успішно повернуто.",
+ "englishSource": "Device key rotated successfully."
+ },
+ "activateHotKeyBannerTitle": {
+ "message": "Активуйте ключ пристрою",
+ "englishSource": "Activate device key"
+ },
+ "activateHotKeyBannerBody": {
+ "message": "Згенеруйте ключ пристрою для цього відновленого облікового запису. Обов'язково перед відправкою.",
+ "englishSource": "Generate a device key for this recovered account. Required before sending."
+ },
+ "activateHotKeyBannerCta": {
+ "message": "активувати",
+ "englishSource": "Activate"
+ },
+ "activateHotKeyBannerError": {
+ "message": "Не вдалося активувати ключ пристрою. Спробуйте знову.",
+ "englishSource": "Failed to activate device key. Try again."
+ },
+ "revealHotKey": {
+ "message": "Розкрити ключ пристрою",
+ "englishSource": "Reveal device key"
+ },
+ "revealHotKeyDescription": {
+ "message": "Ваш (гарячий) ключ пристрою можна обертати в меню «Налаштування» → «Обертати ключ пристрою», тому його розкриття є нижчим, ніж ваш seed phrase — але будь-хто, хто його тримає, може підписувати транзакції, доки ви не повернете його.",
+ "englishSource": "Your device (hot) key is rotatable from Settings → Rotate Device Key, so revealing it is lower-stakes than your seed phrase — but anyone who holds it can sign transactions until you rotate."
+ },
+ "hotPrivateKey": {
+ "message": "Приватний ключ пристрою",
+ "englishSource": "Device Private Key"
+ },
+ "hotPublicKeyLabel": {
+ "message": "Відкритий ключ пристрою",
+ "englishSource": "Device Public Key"
+ },
+ "coldPrivateKey": {
+ "message": "Приватний ключ відновлення (холодний).",
+ "englishSource": "Recovery (Cold) Private Key"
+ },
+ "coldPublicKeyLabel": {
+ "message": "Відновлення (холодного) відкритого ключа",
+ "englishSource": "Recovery (Cold) Public Key"
+ },
+ "guardianKeysRevealDescription": {
+ "message": "Ваш обліковий запис опікуна захищено трьома ключами: ключем пристрою (гарячим) для звичайного підпису, ключем відновлення (холодним), отриманим із вашого seed phrase для операцій керування обліковим записом, і зовнішнім співпідписувачем опікуна. Нижче ваш холодний приватний ключ плюс обидва відкритих ключі. Щоб розкрити закритий ключ пристрою, скористайтеся Налаштуваннями → Відкрити ключ пристрою.",
+ "englishSource": "Your Guardian account is protected by three keys: a device (hot) key for routine signing, a recovery (cold) key derived from your seed phrase for account-management operations, and an external guardian co-signer. Below are your cold private key plus both public keys. To reveal the device private key, use Settings → Reveal Device Key."
+ },
+ "hotKeyRevealWarningTitle": {
+ "message": "Перш ніж відкрити ключ свого пристрою",
+ "englishSource": "Before you reveal your device key"
+ },
+ "hotKeyRevealWarningBody": {
+ "message": "Будь-хто з цим ключем може підписувати транзакції, доки ви його не повернете. Якщо ключ витік, поверніть його через Налаштування → Обернути ключ пристрою.",
+ "englishSource": "Anyone with this key can sign transactions until you rotate it. If the key is leaked, rotate it via Settings → Rotate Device Key."
+ },
"invalidUrl": {
"message": "Введіть дійсну URL-адресу, яка починається з http:// або https://",
"englishSource": "Enter a valid URL starting with http:// or https://"
diff --git a/public/_locales/zh_CN/messages.json b/public/_locales/zh_CN/messages.json
index 3948dafb9..e59c345c9 100644
--- a/public/_locales/zh_CN/messages.json
+++ b/public/_locales/zh_CN/messages.json
@@ -1508,6 +1508,10 @@
"message": "不要与任何人分享。",
"englishSource": "Do not share with anyone."
},
+ "seedPhraseRecoveryCaption": {
+ "message": "您的 seed phrase 是您的恢复密钥 — 用于轮换设备密钥,而不是用于日常交易。",
+ "englishSource": "Your seed phrase is your recovery key — used to rotate device keys, not for everyday transactions."
+ },
"select": {
"message": "选择",
"note": "Onboarding",
@@ -1690,6 +1694,14 @@
"message": "将您的监护人帐户指向新的监护人端点。该切换被签署为链上提案。",
"englishSource": "Point your Guardian account at a new guardian endpoint. The switch is signed as an on-chain proposal."
},
+ "switchGuardianConfirmation": {
+ "message": "该密钥使用您的恢复(冷)密钥进行签名,并由您当前的监护人共同签署。您的资金和账户保持不变。继续?",
+ "englishSource": "This is signed with your recovery (cold) key and co-signed by your current guardian. Your funds and account stay the same. Continue?"
+ },
+ "confirmSwitchGuardian": {
+ "message": "确认切换",
+ "englishSource": "Confirm switch"
+ },
"switchingGuardian": {
"message": "切换监护人...",
"englishSource": "Switching guardian..."
@@ -1702,6 +1714,78 @@
"message": "那已经是你现在的监护人了。",
"englishSource": "That's already your current guardian."
},
+ "replaceHotKey": {
+ "message": "旋转设备密钥",
+ "englishSource": "Rotate device key"
+ },
+ "replaceHotKeyDescription": {
+ "message": "生成一个新的设备(热)密钥并替换链上当前的设备密钥。如果您怀疑您的设备受到威胁,这很有用。",
+ "englishSource": "Generate a new device (hot) key and replace the current one on-chain. Useful if you suspect your device is compromised."
+ },
+ "replaceHotKeyConfirmation": {
+ "message": "这是用您的恢复(冷)密钥签名的。您的 seed phrase 不是必需的,您的资金和帐户保持不变。继续?",
+ "englishSource": "This is signed with your recovery (cold) key. Your seed phrase is not required, and your funds and account stay the same. Continue?"
+ },
+ "confirmReplaceHotKey": {
+ "message": "确认轮换",
+ "englishSource": "Confirm rotation"
+ },
+ "hotKeyRotated": {
+ "message": "设备密钥轮换成功。",
+ "englishSource": "Device key rotated successfully."
+ },
+ "activateHotKeyBannerTitle": {
+ "message": "激活设备密钥",
+ "englishSource": "Activate device key"
+ },
+ "activateHotKeyBannerBody": {
+ "message": "为该恢复的帐户生成设备密钥。发送前需要。",
+ "englishSource": "Generate a device key for this recovered account. Required before sending."
+ },
+ "activateHotKeyBannerCta": {
+ "message": "激活",
+ "englishSource": "Activate"
+ },
+ "activateHotKeyBannerError": {
+ "message": "无法激活设备密钥。再试一次。",
+ "englishSource": "Failed to activate device key. Try again."
+ },
+ "revealHotKey": {
+ "message": "显示设备密钥",
+ "englishSource": "Reveal device key"
+ },
+ "revealHotKeyDescription": {
+ "message": "您的设备(热)密钥可通过“设置”→“旋转设备密钥”进行旋转,因此暴露它的风险比您的 seed phrase 低 - 但持有它的任何人都可以签署交易,直到您旋转为止。",
+ "englishSource": "Your device (hot) key is rotatable from Settings → Rotate Device Key, so revealing it is lower-stakes than your seed phrase — but anyone who holds it can sign transactions until you rotate."
+ },
+ "hotPrivateKey": {
+ "message": "设备私钥",
+ "englishSource": "Device Private Key"
+ },
+ "hotPublicKeyLabel": {
+ "message": "设备公钥",
+ "englishSource": "Device Public Key"
+ },
+ "coldPrivateKey": {
+ "message": "恢复(冷)私钥",
+ "englishSource": "Recovery (Cold) Private Key"
+ },
+ "coldPublicKeyLabel": {
+ "message": "恢复(冷)公钥",
+ "englishSource": "Recovery (Cold) Public Key"
+ },
+ "guardianKeysRevealDescription": {
+ "message": "您的监护人帐户受到三个密钥的保护:用于例行签名的设备(热)密钥、从您的 seed phrase 派生的用于帐户管理操作的恢复(冷)密钥,以及外部监护人共同签名者。下面是您的冷私钥和两个公钥。要显示设备私钥,请使用“设置”→“显示设备密钥”。",
+ "englishSource": "Your Guardian account is protected by three keys: a device (hot) key for routine signing, a recovery (cold) key derived from your seed phrase for account-management operations, and an external guardian co-signer. Below are your cold private key plus both public keys. To reveal the device private key, use Settings → Reveal Device Key."
+ },
+ "hotKeyRevealWarningTitle": {
+ "message": "在透露您的设备密钥之前",
+ "englishSource": "Before you reveal your device key"
+ },
+ "hotKeyRevealWarningBody": {
+ "message": "拥有此密钥的任何人都可以签署交易,直到您轮换它为止。如果密钥泄漏,请通过“设置”→“轮换设备密钥”进行轮换。",
+ "englishSource": "Anyone with this key can sign transactions until you rotate it. If the key is leaked, rotate it via Settings → Rotate Device Key."
+ },
"invalidUrl": {
"message": "输入以 http:// 或 https:// 开头的有效 URL",
"englishSource": "Enter a valid URL starting with http:// or https://"
diff --git a/public/_locales/zh_TW/messages.json b/public/_locales/zh_TW/messages.json
index e91b86828..4550dfb54 100644
--- a/public/_locales/zh_TW/messages.json
+++ b/public/_locales/zh_TW/messages.json
@@ -1499,6 +1499,10 @@
"message": "不要与任何人分享。",
"englishSource": "Do not share with anyone."
},
+ "seedPhraseRecoveryCaption": {
+ "message": "您的 seed phrase 是您的恢复密钥 — 用于轮换设备密钥,而不是用于日常交易。",
+ "englishSource": "Your seed phrase is your recovery key — used to rotate device keys, not for everyday transactions."
+ },
"select": {
"message": "選擇",
"note": "Onboarding",
@@ -1681,6 +1685,14 @@
"message": "将您的监护人帐户指向新的监护人端点。该切换被签署为链上提案。",
"englishSource": "Point your Guardian account at a new guardian endpoint. The switch is signed as an on-chain proposal."
},
+ "switchGuardianConfirmation": {
+ "message": "该密钥使用您的恢复(冷)密钥进行签名,并由您当前的监护人共同签署。您的资金和账户保持不变。继续?",
+ "englishSource": "This is signed with your recovery (cold) key and co-signed by your current guardian. Your funds and account stay the same. Continue?"
+ },
+ "confirmSwitchGuardian": {
+ "message": "确认切换",
+ "englishSource": "Confirm switch"
+ },
"switchingGuardian": {
"message": "切换监护人...",
"englishSource": "Switching guardian..."
@@ -1693,6 +1705,78 @@
"message": "那已经是你现在的监护人了。",
"englishSource": "That's already your current guardian."
},
+ "replaceHotKey": {
+ "message": "旋转设备密钥",
+ "englishSource": "Rotate device key"
+ },
+ "replaceHotKeyDescription": {
+ "message": "生成一个新的设备(热)密钥并替换链上当前的设备密钥。如果您怀疑您的设备受到威胁,这很有用。",
+ "englishSource": "Generate a new device (hot) key and replace the current one on-chain. Useful if you suspect your device is compromised."
+ },
+ "replaceHotKeyConfirmation": {
+ "message": "这是用您的恢复(冷)密钥签名的。您的 seed phrase 不是必需的,您的资金和帐户保持不变。继续?",
+ "englishSource": "This is signed with your recovery (cold) key. Your seed phrase is not required, and your funds and account stay the same. Continue?"
+ },
+ "confirmReplaceHotKey": {
+ "message": "确认轮换",
+ "englishSource": "Confirm rotation"
+ },
+ "hotKeyRotated": {
+ "message": "设备密钥轮换成功。",
+ "englishSource": "Device key rotated successfully."
+ },
+ "activateHotKeyBannerTitle": {
+ "message": "激活设备密钥",
+ "englishSource": "Activate device key"
+ },
+ "activateHotKeyBannerBody": {
+ "message": "为该恢复的帐户生成设备密钥。发送前需要。",
+ "englishSource": "Generate a device key for this recovered account. Required before sending."
+ },
+ "activateHotKeyBannerCta": {
+ "message": "激活",
+ "englishSource": "Activate"
+ },
+ "activateHotKeyBannerError": {
+ "message": "无法激活设备密钥。再试一次。",
+ "englishSource": "Failed to activate device key. Try again."
+ },
+ "revealHotKey": {
+ "message": "显示设备密钥",
+ "englishSource": "Reveal device key"
+ },
+ "revealHotKeyDescription": {
+ "message": "您的设备(热)密钥可通过“设置”→“旋转设备密钥”进行旋转,因此暴露它的风险比您的 seed phrase 低 - 但持有它的任何人都可以签署交易,直到您旋转为止。",
+ "englishSource": "Your device (hot) key is rotatable from Settings → Rotate Device Key, so revealing it is lower-stakes than your seed phrase — but anyone who holds it can sign transactions until you rotate."
+ },
+ "hotPrivateKey": {
+ "message": "设备私钥",
+ "englishSource": "Device Private Key"
+ },
+ "hotPublicKeyLabel": {
+ "message": "设备公钥",
+ "englishSource": "Device Public Key"
+ },
+ "coldPrivateKey": {
+ "message": "恢复(冷)私钥",
+ "englishSource": "Recovery (Cold) Private Key"
+ },
+ "coldPublicKeyLabel": {
+ "message": "恢复(冷)公钥",
+ "englishSource": "Recovery (Cold) Public Key"
+ },
+ "guardianKeysRevealDescription": {
+ "message": "您的监护人帐户受到三个密钥的保护:用于例行签名的设备(热)密钥、从您的 seed phrase 派生的用于帐户管理操作的恢复(冷)密钥,以及外部监护人共同签名者。下面是您的冷私钥和两个公钥。要显示设备私钥,请使用“设置”→“显示设备密钥”。",
+ "englishSource": "Your Guardian account is protected by three keys: a device (hot) key for routine signing, a recovery (cold) key derived from your seed phrase for account-management operations, and an external guardian co-signer. Below are your cold private key plus both public keys. To reveal the device private key, use Settings → Reveal Device Key."
+ },
+ "hotKeyRevealWarningTitle": {
+ "message": "在透露您的设备密钥之前",
+ "englishSource": "Before you reveal your device key"
+ },
+ "hotKeyRevealWarningBody": {
+ "message": "拥有此密钥的任何人都可以签署交易,直到您轮换它为止。如果密钥泄漏,请通过“设置”→“轮换设备密钥”进行轮换。",
+ "englishSource": "Anyone with this key can sign transactions until you rotate it. If the key is leaked, rotate it via Settings → Rotate Device Key."
+ },
"invalidUrl": {
"message": "输入以 http:// 或 https:// 开头的有效 URL",
"englishSource": "Enter a valid URL starting with http:// or https://"
diff --git a/src-tauri/gen/schemas/acl-manifests.json b/src-tauri/gen/schemas/acl-manifests.json
index 86cdb1f5f..f43b85280 100644
--- a/src-tauri/gen/schemas/acl-manifests.json
+++ b/src-tauri/gen/schemas/acl-manifests.json
@@ -1 +1 @@
-{"core":{"default_permission":{"identifier":"default","description":"Default core plugins set.","permissions":["core:path:default","core:event:default","core:window:default","core:webview:default","core:app:default","core:image:default","core:resources:default","core:menu:default","core:tray:default"]},"permissions":{},"permission_sets":{},"global_scope_schema":null},"core:app":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-version","allow-name","allow-tauri-version","allow-identifier","allow-bundle-type","allow-register-listener","allow-remove-listener"]},"permissions":{"allow-app-hide":{"identifier":"allow-app-hide","description":"Enables the app_hide command without any pre-configured scope.","commands":{"allow":["app_hide"],"deny":[]}},"allow-app-show":{"identifier":"allow-app-show","description":"Enables the app_show command without any pre-configured scope.","commands":{"allow":["app_show"],"deny":[]}},"allow-bundle-type":{"identifier":"allow-bundle-type","description":"Enables the bundle_type command without any pre-configured scope.","commands":{"allow":["bundle_type"],"deny":[]}},"allow-default-window-icon":{"identifier":"allow-default-window-icon","description":"Enables the default_window_icon command without any pre-configured scope.","commands":{"allow":["default_window_icon"],"deny":[]}},"allow-fetch-data-store-identifiers":{"identifier":"allow-fetch-data-store-identifiers","description":"Enables the fetch_data_store_identifiers command without any pre-configured scope.","commands":{"allow":["fetch_data_store_identifiers"],"deny":[]}},"allow-identifier":{"identifier":"allow-identifier","description":"Enables the identifier command without any pre-configured scope.","commands":{"allow":["identifier"],"deny":[]}},"allow-name":{"identifier":"allow-name","description":"Enables the name command without any pre-configured scope.","commands":{"allow":["name"],"deny":[]}},"allow-register-listener":{"identifier":"allow-register-listener","description":"Enables the register_listener command without any pre-configured scope.","commands":{"allow":["register_listener"],"deny":[]}},"allow-remove-data-store":{"identifier":"allow-remove-data-store","description":"Enables the remove_data_store command without any pre-configured scope.","commands":{"allow":["remove_data_store"],"deny":[]}},"allow-remove-listener":{"identifier":"allow-remove-listener","description":"Enables the remove_listener command without any pre-configured scope.","commands":{"allow":["remove_listener"],"deny":[]}},"allow-set-app-theme":{"identifier":"allow-set-app-theme","description":"Enables the set_app_theme command without any pre-configured scope.","commands":{"allow":["set_app_theme"],"deny":[]}},"allow-set-dock-visibility":{"identifier":"allow-set-dock-visibility","description":"Enables the set_dock_visibility command without any pre-configured scope.","commands":{"allow":["set_dock_visibility"],"deny":[]}},"allow-tauri-version":{"identifier":"allow-tauri-version","description":"Enables the tauri_version command without any pre-configured scope.","commands":{"allow":["tauri_version"],"deny":[]}},"allow-version":{"identifier":"allow-version","description":"Enables the version command without any pre-configured scope.","commands":{"allow":["version"],"deny":[]}},"deny-app-hide":{"identifier":"deny-app-hide","description":"Denies the app_hide command without any pre-configured scope.","commands":{"allow":[],"deny":["app_hide"]}},"deny-app-show":{"identifier":"deny-app-show","description":"Denies the app_show command without any pre-configured scope.","commands":{"allow":[],"deny":["app_show"]}},"deny-bundle-type":{"identifier":"deny-bundle-type","description":"Denies the bundle_type command without any pre-configured scope.","commands":{"allow":[],"deny":["bundle_type"]}},"deny-default-window-icon":{"identifier":"deny-default-window-icon","description":"Denies the default_window_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["default_window_icon"]}},"deny-fetch-data-store-identifiers":{"identifier":"deny-fetch-data-store-identifiers","description":"Denies the fetch_data_store_identifiers command without any pre-configured scope.","commands":{"allow":[],"deny":["fetch_data_store_identifiers"]}},"deny-identifier":{"identifier":"deny-identifier","description":"Denies the identifier command without any pre-configured scope.","commands":{"allow":[],"deny":["identifier"]}},"deny-name":{"identifier":"deny-name","description":"Denies the name command without any pre-configured scope.","commands":{"allow":[],"deny":["name"]}},"deny-register-listener":{"identifier":"deny-register-listener","description":"Denies the register_listener command without any pre-configured scope.","commands":{"allow":[],"deny":["register_listener"]}},"deny-remove-data-store":{"identifier":"deny-remove-data-store","description":"Denies the remove_data_store command without any pre-configured scope.","commands":{"allow":[],"deny":["remove_data_store"]}},"deny-remove-listener":{"identifier":"deny-remove-listener","description":"Denies the remove_listener command without any pre-configured scope.","commands":{"allow":[],"deny":["remove_listener"]}},"deny-set-app-theme":{"identifier":"deny-set-app-theme","description":"Denies the set_app_theme command without any pre-configured scope.","commands":{"allow":[],"deny":["set_app_theme"]}},"deny-set-dock-visibility":{"identifier":"deny-set-dock-visibility","description":"Denies the set_dock_visibility command without any pre-configured scope.","commands":{"allow":[],"deny":["set_dock_visibility"]}},"deny-tauri-version":{"identifier":"deny-tauri-version","description":"Denies the tauri_version command without any pre-configured scope.","commands":{"allow":[],"deny":["tauri_version"]}},"deny-version":{"identifier":"deny-version","description":"Denies the version command without any pre-configured scope.","commands":{"allow":[],"deny":["version"]}}},"permission_sets":{},"global_scope_schema":null},"core:event":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-listen","allow-unlisten","allow-emit","allow-emit-to"]},"permissions":{"allow-emit":{"identifier":"allow-emit","description":"Enables the emit command without any pre-configured scope.","commands":{"allow":["emit"],"deny":[]}},"allow-emit-to":{"identifier":"allow-emit-to","description":"Enables the emit_to command without any pre-configured scope.","commands":{"allow":["emit_to"],"deny":[]}},"allow-listen":{"identifier":"allow-listen","description":"Enables the listen command without any pre-configured scope.","commands":{"allow":["listen"],"deny":[]}},"allow-unlisten":{"identifier":"allow-unlisten","description":"Enables the unlisten command without any pre-configured scope.","commands":{"allow":["unlisten"],"deny":[]}},"deny-emit":{"identifier":"deny-emit","description":"Denies the emit command without any pre-configured scope.","commands":{"allow":[],"deny":["emit"]}},"deny-emit-to":{"identifier":"deny-emit-to","description":"Denies the emit_to command without any pre-configured scope.","commands":{"allow":[],"deny":["emit_to"]}},"deny-listen":{"identifier":"deny-listen","description":"Denies the listen command without any pre-configured scope.","commands":{"allow":[],"deny":["listen"]}},"deny-unlisten":{"identifier":"deny-unlisten","description":"Denies the unlisten command without any pre-configured scope.","commands":{"allow":[],"deny":["unlisten"]}}},"permission_sets":{},"global_scope_schema":null},"core:image":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-new","allow-from-bytes","allow-from-path","allow-rgba","allow-size"]},"permissions":{"allow-from-bytes":{"identifier":"allow-from-bytes","description":"Enables the from_bytes command without any pre-configured scope.","commands":{"allow":["from_bytes"],"deny":[]}},"allow-from-path":{"identifier":"allow-from-path","description":"Enables the from_path command without any pre-configured scope.","commands":{"allow":["from_path"],"deny":[]}},"allow-new":{"identifier":"allow-new","description":"Enables the new command without any pre-configured scope.","commands":{"allow":["new"],"deny":[]}},"allow-rgba":{"identifier":"allow-rgba","description":"Enables the rgba command without any pre-configured scope.","commands":{"allow":["rgba"],"deny":[]}},"allow-size":{"identifier":"allow-size","description":"Enables the size command without any pre-configured scope.","commands":{"allow":["size"],"deny":[]}},"deny-from-bytes":{"identifier":"deny-from-bytes","description":"Denies the from_bytes command without any pre-configured scope.","commands":{"allow":[],"deny":["from_bytes"]}},"deny-from-path":{"identifier":"deny-from-path","description":"Denies the from_path command without any pre-configured scope.","commands":{"allow":[],"deny":["from_path"]}},"deny-new":{"identifier":"deny-new","description":"Denies the new command without any pre-configured scope.","commands":{"allow":[],"deny":["new"]}},"deny-rgba":{"identifier":"deny-rgba","description":"Denies the rgba command without any pre-configured scope.","commands":{"allow":[],"deny":["rgba"]}},"deny-size":{"identifier":"deny-size","description":"Denies the size command without any pre-configured scope.","commands":{"allow":[],"deny":["size"]}}},"permission_sets":{},"global_scope_schema":null},"core:menu":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-new","allow-append","allow-prepend","allow-insert","allow-remove","allow-remove-at","allow-items","allow-get","allow-popup","allow-create-default","allow-set-as-app-menu","allow-set-as-window-menu","allow-text","allow-set-text","allow-is-enabled","allow-set-enabled","allow-set-accelerator","allow-set-as-windows-menu-for-nsapp","allow-set-as-help-menu-for-nsapp","allow-is-checked","allow-set-checked","allow-set-icon"]},"permissions":{"allow-append":{"identifier":"allow-append","description":"Enables the append command without any pre-configured scope.","commands":{"allow":["append"],"deny":[]}},"allow-create-default":{"identifier":"allow-create-default","description":"Enables the create_default command without any pre-configured scope.","commands":{"allow":["create_default"],"deny":[]}},"allow-get":{"identifier":"allow-get","description":"Enables the get command without any pre-configured scope.","commands":{"allow":["get"],"deny":[]}},"allow-insert":{"identifier":"allow-insert","description":"Enables the insert command without any pre-configured scope.","commands":{"allow":["insert"],"deny":[]}},"allow-is-checked":{"identifier":"allow-is-checked","description":"Enables the is_checked command without any pre-configured scope.","commands":{"allow":["is_checked"],"deny":[]}},"allow-is-enabled":{"identifier":"allow-is-enabled","description":"Enables the is_enabled command without any pre-configured scope.","commands":{"allow":["is_enabled"],"deny":[]}},"allow-items":{"identifier":"allow-items","description":"Enables the items command without any pre-configured scope.","commands":{"allow":["items"],"deny":[]}},"allow-new":{"identifier":"allow-new","description":"Enables the new command without any pre-configured scope.","commands":{"allow":["new"],"deny":[]}},"allow-popup":{"identifier":"allow-popup","description":"Enables the popup command without any pre-configured scope.","commands":{"allow":["popup"],"deny":[]}},"allow-prepend":{"identifier":"allow-prepend","description":"Enables the prepend command without any pre-configured scope.","commands":{"allow":["prepend"],"deny":[]}},"allow-remove":{"identifier":"allow-remove","description":"Enables the remove command without any pre-configured scope.","commands":{"allow":["remove"],"deny":[]}},"allow-remove-at":{"identifier":"allow-remove-at","description":"Enables the remove_at command without any pre-configured scope.","commands":{"allow":["remove_at"],"deny":[]}},"allow-set-accelerator":{"identifier":"allow-set-accelerator","description":"Enables the set_accelerator command without any pre-configured scope.","commands":{"allow":["set_accelerator"],"deny":[]}},"allow-set-as-app-menu":{"identifier":"allow-set-as-app-menu","description":"Enables the set_as_app_menu command without any pre-configured scope.","commands":{"allow":["set_as_app_menu"],"deny":[]}},"allow-set-as-help-menu-for-nsapp":{"identifier":"allow-set-as-help-menu-for-nsapp","description":"Enables the set_as_help_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":["set_as_help_menu_for_nsapp"],"deny":[]}},"allow-set-as-window-menu":{"identifier":"allow-set-as-window-menu","description":"Enables the set_as_window_menu command without any pre-configured scope.","commands":{"allow":["set_as_window_menu"],"deny":[]}},"allow-set-as-windows-menu-for-nsapp":{"identifier":"allow-set-as-windows-menu-for-nsapp","description":"Enables the set_as_windows_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":["set_as_windows_menu_for_nsapp"],"deny":[]}},"allow-set-checked":{"identifier":"allow-set-checked","description":"Enables the set_checked command without any pre-configured scope.","commands":{"allow":["set_checked"],"deny":[]}},"allow-set-enabled":{"identifier":"allow-set-enabled","description":"Enables the set_enabled command without any pre-configured scope.","commands":{"allow":["set_enabled"],"deny":[]}},"allow-set-icon":{"identifier":"allow-set-icon","description":"Enables the set_icon command without any pre-configured scope.","commands":{"allow":["set_icon"],"deny":[]}},"allow-set-text":{"identifier":"allow-set-text","description":"Enables the set_text command without any pre-configured scope.","commands":{"allow":["set_text"],"deny":[]}},"allow-text":{"identifier":"allow-text","description":"Enables the text command without any pre-configured scope.","commands":{"allow":["text"],"deny":[]}},"deny-append":{"identifier":"deny-append","description":"Denies the append command without any pre-configured scope.","commands":{"allow":[],"deny":["append"]}},"deny-create-default":{"identifier":"deny-create-default","description":"Denies the create_default command without any pre-configured scope.","commands":{"allow":[],"deny":["create_default"]}},"deny-get":{"identifier":"deny-get","description":"Denies the get command without any pre-configured scope.","commands":{"allow":[],"deny":["get"]}},"deny-insert":{"identifier":"deny-insert","description":"Denies the insert command without any pre-configured scope.","commands":{"allow":[],"deny":["insert"]}},"deny-is-checked":{"identifier":"deny-is-checked","description":"Denies the is_checked command without any pre-configured scope.","commands":{"allow":[],"deny":["is_checked"]}},"deny-is-enabled":{"identifier":"deny-is-enabled","description":"Denies the is_enabled command without any pre-configured scope.","commands":{"allow":[],"deny":["is_enabled"]}},"deny-items":{"identifier":"deny-items","description":"Denies the items command without any pre-configured scope.","commands":{"allow":[],"deny":["items"]}},"deny-new":{"identifier":"deny-new","description":"Denies the new command without any pre-configured scope.","commands":{"allow":[],"deny":["new"]}},"deny-popup":{"identifier":"deny-popup","description":"Denies the popup command without any pre-configured scope.","commands":{"allow":[],"deny":["popup"]}},"deny-prepend":{"identifier":"deny-prepend","description":"Denies the prepend command without any pre-configured scope.","commands":{"allow":[],"deny":["prepend"]}},"deny-remove":{"identifier":"deny-remove","description":"Denies the remove command without any pre-configured scope.","commands":{"allow":[],"deny":["remove"]}},"deny-remove-at":{"identifier":"deny-remove-at","description":"Denies the remove_at command without any pre-configured scope.","commands":{"allow":[],"deny":["remove_at"]}},"deny-set-accelerator":{"identifier":"deny-set-accelerator","description":"Denies the set_accelerator command without any pre-configured scope.","commands":{"allow":[],"deny":["set_accelerator"]}},"deny-set-as-app-menu":{"identifier":"deny-set-as-app-menu","description":"Denies the set_as_app_menu command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_app_menu"]}},"deny-set-as-help-menu-for-nsapp":{"identifier":"deny-set-as-help-menu-for-nsapp","description":"Denies the set_as_help_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_help_menu_for_nsapp"]}},"deny-set-as-window-menu":{"identifier":"deny-set-as-window-menu","description":"Denies the set_as_window_menu command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_window_menu"]}},"deny-set-as-windows-menu-for-nsapp":{"identifier":"deny-set-as-windows-menu-for-nsapp","description":"Denies the set_as_windows_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_windows_menu_for_nsapp"]}},"deny-set-checked":{"identifier":"deny-set-checked","description":"Denies the set_checked command without any pre-configured scope.","commands":{"allow":[],"deny":["set_checked"]}},"deny-set-enabled":{"identifier":"deny-set-enabled","description":"Denies the set_enabled command without any pre-configured scope.","commands":{"allow":[],"deny":["set_enabled"]}},"deny-set-icon":{"identifier":"deny-set-icon","description":"Denies the set_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon"]}},"deny-set-text":{"identifier":"deny-set-text","description":"Denies the set_text command without any pre-configured scope.","commands":{"allow":[],"deny":["set_text"]}},"deny-text":{"identifier":"deny-text","description":"Denies the text command without any pre-configured scope.","commands":{"allow":[],"deny":["text"]}}},"permission_sets":{},"global_scope_schema":null},"core:path":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-resolve-directory","allow-resolve","allow-normalize","allow-join","allow-dirname","allow-extname","allow-basename","allow-is-absolute"]},"permissions":{"allow-basename":{"identifier":"allow-basename","description":"Enables the basename command without any pre-configured scope.","commands":{"allow":["basename"],"deny":[]}},"allow-dirname":{"identifier":"allow-dirname","description":"Enables the dirname command without any pre-configured scope.","commands":{"allow":["dirname"],"deny":[]}},"allow-extname":{"identifier":"allow-extname","description":"Enables the extname command without any pre-configured scope.","commands":{"allow":["extname"],"deny":[]}},"allow-is-absolute":{"identifier":"allow-is-absolute","description":"Enables the is_absolute command without any pre-configured scope.","commands":{"allow":["is_absolute"],"deny":[]}},"allow-join":{"identifier":"allow-join","description":"Enables the join command without any pre-configured scope.","commands":{"allow":["join"],"deny":[]}},"allow-normalize":{"identifier":"allow-normalize","description":"Enables the normalize command without any pre-configured scope.","commands":{"allow":["normalize"],"deny":[]}},"allow-resolve":{"identifier":"allow-resolve","description":"Enables the resolve command without any pre-configured scope.","commands":{"allow":["resolve"],"deny":[]}},"allow-resolve-directory":{"identifier":"allow-resolve-directory","description":"Enables the resolve_directory command without any pre-configured scope.","commands":{"allow":["resolve_directory"],"deny":[]}},"deny-basename":{"identifier":"deny-basename","description":"Denies the basename command without any pre-configured scope.","commands":{"allow":[],"deny":["basename"]}},"deny-dirname":{"identifier":"deny-dirname","description":"Denies the dirname command without any pre-configured scope.","commands":{"allow":[],"deny":["dirname"]}},"deny-extname":{"identifier":"deny-extname","description":"Denies the extname command without any pre-configured scope.","commands":{"allow":[],"deny":["extname"]}},"deny-is-absolute":{"identifier":"deny-is-absolute","description":"Denies the is_absolute command without any pre-configured scope.","commands":{"allow":[],"deny":["is_absolute"]}},"deny-join":{"identifier":"deny-join","description":"Denies the join command without any pre-configured scope.","commands":{"allow":[],"deny":["join"]}},"deny-normalize":{"identifier":"deny-normalize","description":"Denies the normalize command without any pre-configured scope.","commands":{"allow":[],"deny":["normalize"]}},"deny-resolve":{"identifier":"deny-resolve","description":"Denies the resolve command without any pre-configured scope.","commands":{"allow":[],"deny":["resolve"]}},"deny-resolve-directory":{"identifier":"deny-resolve-directory","description":"Denies the resolve_directory command without any pre-configured scope.","commands":{"allow":[],"deny":["resolve_directory"]}}},"permission_sets":{},"global_scope_schema":null},"core:resources":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-close"]},"permissions":{"allow-close":{"identifier":"allow-close","description":"Enables the close command without any pre-configured scope.","commands":{"allow":["close"],"deny":[]}},"deny-close":{"identifier":"deny-close","description":"Denies the close command without any pre-configured scope.","commands":{"allow":[],"deny":["close"]}}},"permission_sets":{},"global_scope_schema":null},"core:tray":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-new","allow-get-by-id","allow-remove-by-id","allow-set-icon","allow-set-menu","allow-set-tooltip","allow-set-title","allow-set-visible","allow-set-temp-dir-path","allow-set-icon-as-template","allow-set-show-menu-on-left-click"]},"permissions":{"allow-get-by-id":{"identifier":"allow-get-by-id","description":"Enables the get_by_id command without any pre-configured scope.","commands":{"allow":["get_by_id"],"deny":[]}},"allow-new":{"identifier":"allow-new","description":"Enables the new command without any pre-configured scope.","commands":{"allow":["new"],"deny":[]}},"allow-remove-by-id":{"identifier":"allow-remove-by-id","description":"Enables the remove_by_id command without any pre-configured scope.","commands":{"allow":["remove_by_id"],"deny":[]}},"allow-set-icon":{"identifier":"allow-set-icon","description":"Enables the set_icon command without any pre-configured scope.","commands":{"allow":["set_icon"],"deny":[]}},"allow-set-icon-as-template":{"identifier":"allow-set-icon-as-template","description":"Enables the set_icon_as_template command without any pre-configured scope.","commands":{"allow":["set_icon_as_template"],"deny":[]}},"allow-set-menu":{"identifier":"allow-set-menu","description":"Enables the set_menu command without any pre-configured scope.","commands":{"allow":["set_menu"],"deny":[]}},"allow-set-show-menu-on-left-click":{"identifier":"allow-set-show-menu-on-left-click","description":"Enables the set_show_menu_on_left_click command without any pre-configured scope.","commands":{"allow":["set_show_menu_on_left_click"],"deny":[]}},"allow-set-temp-dir-path":{"identifier":"allow-set-temp-dir-path","description":"Enables the set_temp_dir_path command without any pre-configured scope.","commands":{"allow":["set_temp_dir_path"],"deny":[]}},"allow-set-title":{"identifier":"allow-set-title","description":"Enables the set_title command without any pre-configured scope.","commands":{"allow":["set_title"],"deny":[]}},"allow-set-tooltip":{"identifier":"allow-set-tooltip","description":"Enables the set_tooltip command without any pre-configured scope.","commands":{"allow":["set_tooltip"],"deny":[]}},"allow-set-visible":{"identifier":"allow-set-visible","description":"Enables the set_visible command without any pre-configured scope.","commands":{"allow":["set_visible"],"deny":[]}},"deny-get-by-id":{"identifier":"deny-get-by-id","description":"Denies the get_by_id command without any pre-configured scope.","commands":{"allow":[],"deny":["get_by_id"]}},"deny-new":{"identifier":"deny-new","description":"Denies the new command without any pre-configured scope.","commands":{"allow":[],"deny":["new"]}},"deny-remove-by-id":{"identifier":"deny-remove-by-id","description":"Denies the remove_by_id command without any pre-configured scope.","commands":{"allow":[],"deny":["remove_by_id"]}},"deny-set-icon":{"identifier":"deny-set-icon","description":"Denies the set_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon"]}},"deny-set-icon-as-template":{"identifier":"deny-set-icon-as-template","description":"Denies the set_icon_as_template command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon_as_template"]}},"deny-set-menu":{"identifier":"deny-set-menu","description":"Denies the set_menu command without any pre-configured scope.","commands":{"allow":[],"deny":["set_menu"]}},"deny-set-show-menu-on-left-click":{"identifier":"deny-set-show-menu-on-left-click","description":"Denies the set_show_menu_on_left_click command without any pre-configured scope.","commands":{"allow":[],"deny":["set_show_menu_on_left_click"]}},"deny-set-temp-dir-path":{"identifier":"deny-set-temp-dir-path","description":"Denies the set_temp_dir_path command without any pre-configured scope.","commands":{"allow":[],"deny":["set_temp_dir_path"]}},"deny-set-title":{"identifier":"deny-set-title","description":"Denies the set_title command without any pre-configured scope.","commands":{"allow":[],"deny":["set_title"]}},"deny-set-tooltip":{"identifier":"deny-set-tooltip","description":"Denies the set_tooltip command without any pre-configured scope.","commands":{"allow":[],"deny":["set_tooltip"]}},"deny-set-visible":{"identifier":"deny-set-visible","description":"Denies the set_visible command without any pre-configured scope.","commands":{"allow":[],"deny":["set_visible"]}}},"permission_sets":{},"global_scope_schema":null},"core:webview":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-get-all-webviews","allow-webview-position","allow-webview-size","allow-internal-toggle-devtools"]},"permissions":{"allow-clear-all-browsing-data":{"identifier":"allow-clear-all-browsing-data","description":"Enables the clear_all_browsing_data command without any pre-configured scope.","commands":{"allow":["clear_all_browsing_data"],"deny":[]}},"allow-create-webview":{"identifier":"allow-create-webview","description":"Enables the create_webview command without any pre-configured scope.","commands":{"allow":["create_webview"],"deny":[]}},"allow-create-webview-window":{"identifier":"allow-create-webview-window","description":"Enables the create_webview_window command without any pre-configured scope.","commands":{"allow":["create_webview_window"],"deny":[]}},"allow-get-all-webviews":{"identifier":"allow-get-all-webviews","description":"Enables the get_all_webviews command without any pre-configured scope.","commands":{"allow":["get_all_webviews"],"deny":[]}},"allow-internal-toggle-devtools":{"identifier":"allow-internal-toggle-devtools","description":"Enables the internal_toggle_devtools command without any pre-configured scope.","commands":{"allow":["internal_toggle_devtools"],"deny":[]}},"allow-print":{"identifier":"allow-print","description":"Enables the print command without any pre-configured scope.","commands":{"allow":["print"],"deny":[]}},"allow-reparent":{"identifier":"allow-reparent","description":"Enables the reparent command without any pre-configured scope.","commands":{"allow":["reparent"],"deny":[]}},"allow-set-webview-auto-resize":{"identifier":"allow-set-webview-auto-resize","description":"Enables the set_webview_auto_resize command without any pre-configured scope.","commands":{"allow":["set_webview_auto_resize"],"deny":[]}},"allow-set-webview-background-color":{"identifier":"allow-set-webview-background-color","description":"Enables the set_webview_background_color command without any pre-configured scope.","commands":{"allow":["set_webview_background_color"],"deny":[]}},"allow-set-webview-focus":{"identifier":"allow-set-webview-focus","description":"Enables the set_webview_focus command without any pre-configured scope.","commands":{"allow":["set_webview_focus"],"deny":[]}},"allow-set-webview-position":{"identifier":"allow-set-webview-position","description":"Enables the set_webview_position command without any pre-configured scope.","commands":{"allow":["set_webview_position"],"deny":[]}},"allow-set-webview-size":{"identifier":"allow-set-webview-size","description":"Enables the set_webview_size command without any pre-configured scope.","commands":{"allow":["set_webview_size"],"deny":[]}},"allow-set-webview-zoom":{"identifier":"allow-set-webview-zoom","description":"Enables the set_webview_zoom command without any pre-configured scope.","commands":{"allow":["set_webview_zoom"],"deny":[]}},"allow-webview-close":{"identifier":"allow-webview-close","description":"Enables the webview_close command without any pre-configured scope.","commands":{"allow":["webview_close"],"deny":[]}},"allow-webview-hide":{"identifier":"allow-webview-hide","description":"Enables the webview_hide command without any pre-configured scope.","commands":{"allow":["webview_hide"],"deny":[]}},"allow-webview-position":{"identifier":"allow-webview-position","description":"Enables the webview_position command without any pre-configured scope.","commands":{"allow":["webview_position"],"deny":[]}},"allow-webview-show":{"identifier":"allow-webview-show","description":"Enables the webview_show command without any pre-configured scope.","commands":{"allow":["webview_show"],"deny":[]}},"allow-webview-size":{"identifier":"allow-webview-size","description":"Enables the webview_size command without any pre-configured scope.","commands":{"allow":["webview_size"],"deny":[]}},"deny-clear-all-browsing-data":{"identifier":"deny-clear-all-browsing-data","description":"Denies the clear_all_browsing_data command without any pre-configured scope.","commands":{"allow":[],"deny":["clear_all_browsing_data"]}},"deny-create-webview":{"identifier":"deny-create-webview","description":"Denies the create_webview command without any pre-configured scope.","commands":{"allow":[],"deny":["create_webview"]}},"deny-create-webview-window":{"identifier":"deny-create-webview-window","description":"Denies the create_webview_window command without any pre-configured scope.","commands":{"allow":[],"deny":["create_webview_window"]}},"deny-get-all-webviews":{"identifier":"deny-get-all-webviews","description":"Denies the get_all_webviews command without any pre-configured scope.","commands":{"allow":[],"deny":["get_all_webviews"]}},"deny-internal-toggle-devtools":{"identifier":"deny-internal-toggle-devtools","description":"Denies the internal_toggle_devtools command without any pre-configured scope.","commands":{"allow":[],"deny":["internal_toggle_devtools"]}},"deny-print":{"identifier":"deny-print","description":"Denies the print command without any pre-configured scope.","commands":{"allow":[],"deny":["print"]}},"deny-reparent":{"identifier":"deny-reparent","description":"Denies the reparent command without any pre-configured scope.","commands":{"allow":[],"deny":["reparent"]}},"deny-set-webview-auto-resize":{"identifier":"deny-set-webview-auto-resize","description":"Denies the set_webview_auto_resize command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_auto_resize"]}},"deny-set-webview-background-color":{"identifier":"deny-set-webview-background-color","description":"Denies the set_webview_background_color command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_background_color"]}},"deny-set-webview-focus":{"identifier":"deny-set-webview-focus","description":"Denies the set_webview_focus command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_focus"]}},"deny-set-webview-position":{"identifier":"deny-set-webview-position","description":"Denies the set_webview_position command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_position"]}},"deny-set-webview-size":{"identifier":"deny-set-webview-size","description":"Denies the set_webview_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_size"]}},"deny-set-webview-zoom":{"identifier":"deny-set-webview-zoom","description":"Denies the set_webview_zoom command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_zoom"]}},"deny-webview-close":{"identifier":"deny-webview-close","description":"Denies the webview_close command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_close"]}},"deny-webview-hide":{"identifier":"deny-webview-hide","description":"Denies the webview_hide command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_hide"]}},"deny-webview-position":{"identifier":"deny-webview-position","description":"Denies the webview_position command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_position"]}},"deny-webview-show":{"identifier":"deny-webview-show","description":"Denies the webview_show command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_show"]}},"deny-webview-size":{"identifier":"deny-webview-size","description":"Denies the webview_size command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_size"]}}},"permission_sets":{},"global_scope_schema":null},"core:window":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-get-all-windows","allow-scale-factor","allow-inner-position","allow-outer-position","allow-inner-size","allow-outer-size","allow-is-fullscreen","allow-is-minimized","allow-is-maximized","allow-is-focused","allow-is-decorated","allow-is-resizable","allow-is-maximizable","allow-is-minimizable","allow-is-closable","allow-is-visible","allow-is-enabled","allow-title","allow-current-monitor","allow-primary-monitor","allow-monitor-from-point","allow-available-monitors","allow-cursor-position","allow-theme","allow-is-always-on-top","allow-internal-toggle-maximize"]},"permissions":{"allow-available-monitors":{"identifier":"allow-available-monitors","description":"Enables the available_monitors command without any pre-configured scope.","commands":{"allow":["available_monitors"],"deny":[]}},"allow-center":{"identifier":"allow-center","description":"Enables the center command without any pre-configured scope.","commands":{"allow":["center"],"deny":[]}},"allow-close":{"identifier":"allow-close","description":"Enables the close command without any pre-configured scope.","commands":{"allow":["close"],"deny":[]}},"allow-create":{"identifier":"allow-create","description":"Enables the create command without any pre-configured scope.","commands":{"allow":["create"],"deny":[]}},"allow-current-monitor":{"identifier":"allow-current-monitor","description":"Enables the current_monitor command without any pre-configured scope.","commands":{"allow":["current_monitor"],"deny":[]}},"allow-cursor-position":{"identifier":"allow-cursor-position","description":"Enables the cursor_position command without any pre-configured scope.","commands":{"allow":["cursor_position"],"deny":[]}},"allow-destroy":{"identifier":"allow-destroy","description":"Enables the destroy command without any pre-configured scope.","commands":{"allow":["destroy"],"deny":[]}},"allow-get-all-windows":{"identifier":"allow-get-all-windows","description":"Enables the get_all_windows command without any pre-configured scope.","commands":{"allow":["get_all_windows"],"deny":[]}},"allow-hide":{"identifier":"allow-hide","description":"Enables the hide command without any pre-configured scope.","commands":{"allow":["hide"],"deny":[]}},"allow-inner-position":{"identifier":"allow-inner-position","description":"Enables the inner_position command without any pre-configured scope.","commands":{"allow":["inner_position"],"deny":[]}},"allow-inner-size":{"identifier":"allow-inner-size","description":"Enables the inner_size command without any pre-configured scope.","commands":{"allow":["inner_size"],"deny":[]}},"allow-internal-toggle-maximize":{"identifier":"allow-internal-toggle-maximize","description":"Enables the internal_toggle_maximize command without any pre-configured scope.","commands":{"allow":["internal_toggle_maximize"],"deny":[]}},"allow-is-always-on-top":{"identifier":"allow-is-always-on-top","description":"Enables the is_always_on_top command without any pre-configured scope.","commands":{"allow":["is_always_on_top"],"deny":[]}},"allow-is-closable":{"identifier":"allow-is-closable","description":"Enables the is_closable command without any pre-configured scope.","commands":{"allow":["is_closable"],"deny":[]}},"allow-is-decorated":{"identifier":"allow-is-decorated","description":"Enables the is_decorated command without any pre-configured scope.","commands":{"allow":["is_decorated"],"deny":[]}},"allow-is-enabled":{"identifier":"allow-is-enabled","description":"Enables the is_enabled command without any pre-configured scope.","commands":{"allow":["is_enabled"],"deny":[]}},"allow-is-focused":{"identifier":"allow-is-focused","description":"Enables the is_focused command without any pre-configured scope.","commands":{"allow":["is_focused"],"deny":[]}},"allow-is-fullscreen":{"identifier":"allow-is-fullscreen","description":"Enables the is_fullscreen command without any pre-configured scope.","commands":{"allow":["is_fullscreen"],"deny":[]}},"allow-is-maximizable":{"identifier":"allow-is-maximizable","description":"Enables the is_maximizable command without any pre-configured scope.","commands":{"allow":["is_maximizable"],"deny":[]}},"allow-is-maximized":{"identifier":"allow-is-maximized","description":"Enables the is_maximized command without any pre-configured scope.","commands":{"allow":["is_maximized"],"deny":[]}},"allow-is-minimizable":{"identifier":"allow-is-minimizable","description":"Enables the is_minimizable command without any pre-configured scope.","commands":{"allow":["is_minimizable"],"deny":[]}},"allow-is-minimized":{"identifier":"allow-is-minimized","description":"Enables the is_minimized command without any pre-configured scope.","commands":{"allow":["is_minimized"],"deny":[]}},"allow-is-resizable":{"identifier":"allow-is-resizable","description":"Enables the is_resizable command without any pre-configured scope.","commands":{"allow":["is_resizable"],"deny":[]}},"allow-is-visible":{"identifier":"allow-is-visible","description":"Enables the is_visible command without any pre-configured scope.","commands":{"allow":["is_visible"],"deny":[]}},"allow-maximize":{"identifier":"allow-maximize","description":"Enables the maximize command without any pre-configured scope.","commands":{"allow":["maximize"],"deny":[]}},"allow-minimize":{"identifier":"allow-minimize","description":"Enables the minimize command without any pre-configured scope.","commands":{"allow":["minimize"],"deny":[]}},"allow-monitor-from-point":{"identifier":"allow-monitor-from-point","description":"Enables the monitor_from_point command without any pre-configured scope.","commands":{"allow":["monitor_from_point"],"deny":[]}},"allow-outer-position":{"identifier":"allow-outer-position","description":"Enables the outer_position command without any pre-configured scope.","commands":{"allow":["outer_position"],"deny":[]}},"allow-outer-size":{"identifier":"allow-outer-size","description":"Enables the outer_size command without any pre-configured scope.","commands":{"allow":["outer_size"],"deny":[]}},"allow-primary-monitor":{"identifier":"allow-primary-monitor","description":"Enables the primary_monitor command without any pre-configured scope.","commands":{"allow":["primary_monitor"],"deny":[]}},"allow-request-user-attention":{"identifier":"allow-request-user-attention","description":"Enables the request_user_attention command without any pre-configured scope.","commands":{"allow":["request_user_attention"],"deny":[]}},"allow-scale-factor":{"identifier":"allow-scale-factor","description":"Enables the scale_factor command without any pre-configured scope.","commands":{"allow":["scale_factor"],"deny":[]}},"allow-set-always-on-bottom":{"identifier":"allow-set-always-on-bottom","description":"Enables the set_always_on_bottom command without any pre-configured scope.","commands":{"allow":["set_always_on_bottom"],"deny":[]}},"allow-set-always-on-top":{"identifier":"allow-set-always-on-top","description":"Enables the set_always_on_top command without any pre-configured scope.","commands":{"allow":["set_always_on_top"],"deny":[]}},"allow-set-background-color":{"identifier":"allow-set-background-color","description":"Enables the set_background_color command without any pre-configured scope.","commands":{"allow":["set_background_color"],"deny":[]}},"allow-set-badge-count":{"identifier":"allow-set-badge-count","description":"Enables the set_badge_count command without any pre-configured scope.","commands":{"allow":["set_badge_count"],"deny":[]}},"allow-set-badge-label":{"identifier":"allow-set-badge-label","description":"Enables the set_badge_label command without any pre-configured scope.","commands":{"allow":["set_badge_label"],"deny":[]}},"allow-set-closable":{"identifier":"allow-set-closable","description":"Enables the set_closable command without any pre-configured scope.","commands":{"allow":["set_closable"],"deny":[]}},"allow-set-content-protected":{"identifier":"allow-set-content-protected","description":"Enables the set_content_protected command without any pre-configured scope.","commands":{"allow":["set_content_protected"],"deny":[]}},"allow-set-cursor-grab":{"identifier":"allow-set-cursor-grab","description":"Enables the set_cursor_grab command without any pre-configured scope.","commands":{"allow":["set_cursor_grab"],"deny":[]}},"allow-set-cursor-icon":{"identifier":"allow-set-cursor-icon","description":"Enables the set_cursor_icon command without any pre-configured scope.","commands":{"allow":["set_cursor_icon"],"deny":[]}},"allow-set-cursor-position":{"identifier":"allow-set-cursor-position","description":"Enables the set_cursor_position command without any pre-configured scope.","commands":{"allow":["set_cursor_position"],"deny":[]}},"allow-set-cursor-visible":{"identifier":"allow-set-cursor-visible","description":"Enables the set_cursor_visible command without any pre-configured scope.","commands":{"allow":["set_cursor_visible"],"deny":[]}},"allow-set-decorations":{"identifier":"allow-set-decorations","description":"Enables the set_decorations command without any pre-configured scope.","commands":{"allow":["set_decorations"],"deny":[]}},"allow-set-effects":{"identifier":"allow-set-effects","description":"Enables the set_effects command without any pre-configured scope.","commands":{"allow":["set_effects"],"deny":[]}},"allow-set-enabled":{"identifier":"allow-set-enabled","description":"Enables the set_enabled command without any pre-configured scope.","commands":{"allow":["set_enabled"],"deny":[]}},"allow-set-focus":{"identifier":"allow-set-focus","description":"Enables the set_focus command without any pre-configured scope.","commands":{"allow":["set_focus"],"deny":[]}},"allow-set-focusable":{"identifier":"allow-set-focusable","description":"Enables the set_focusable command without any pre-configured scope.","commands":{"allow":["set_focusable"],"deny":[]}},"allow-set-fullscreen":{"identifier":"allow-set-fullscreen","description":"Enables the set_fullscreen command without any pre-configured scope.","commands":{"allow":["set_fullscreen"],"deny":[]}},"allow-set-icon":{"identifier":"allow-set-icon","description":"Enables the set_icon command without any pre-configured scope.","commands":{"allow":["set_icon"],"deny":[]}},"allow-set-ignore-cursor-events":{"identifier":"allow-set-ignore-cursor-events","description":"Enables the set_ignore_cursor_events command without any pre-configured scope.","commands":{"allow":["set_ignore_cursor_events"],"deny":[]}},"allow-set-max-size":{"identifier":"allow-set-max-size","description":"Enables the set_max_size command without any pre-configured scope.","commands":{"allow":["set_max_size"],"deny":[]}},"allow-set-maximizable":{"identifier":"allow-set-maximizable","description":"Enables the set_maximizable command without any pre-configured scope.","commands":{"allow":["set_maximizable"],"deny":[]}},"allow-set-min-size":{"identifier":"allow-set-min-size","description":"Enables the set_min_size command without any pre-configured scope.","commands":{"allow":["set_min_size"],"deny":[]}},"allow-set-minimizable":{"identifier":"allow-set-minimizable","description":"Enables the set_minimizable command without any pre-configured scope.","commands":{"allow":["set_minimizable"],"deny":[]}},"allow-set-overlay-icon":{"identifier":"allow-set-overlay-icon","description":"Enables the set_overlay_icon command without any pre-configured scope.","commands":{"allow":["set_overlay_icon"],"deny":[]}},"allow-set-position":{"identifier":"allow-set-position","description":"Enables the set_position command without any pre-configured scope.","commands":{"allow":["set_position"],"deny":[]}},"allow-set-progress-bar":{"identifier":"allow-set-progress-bar","description":"Enables the set_progress_bar command without any pre-configured scope.","commands":{"allow":["set_progress_bar"],"deny":[]}},"allow-set-resizable":{"identifier":"allow-set-resizable","description":"Enables the set_resizable command without any pre-configured scope.","commands":{"allow":["set_resizable"],"deny":[]}},"allow-set-shadow":{"identifier":"allow-set-shadow","description":"Enables the set_shadow command without any pre-configured scope.","commands":{"allow":["set_shadow"],"deny":[]}},"allow-set-simple-fullscreen":{"identifier":"allow-set-simple-fullscreen","description":"Enables the set_simple_fullscreen command without any pre-configured scope.","commands":{"allow":["set_simple_fullscreen"],"deny":[]}},"allow-set-size":{"identifier":"allow-set-size","description":"Enables the set_size command without any pre-configured scope.","commands":{"allow":["set_size"],"deny":[]}},"allow-set-size-constraints":{"identifier":"allow-set-size-constraints","description":"Enables the set_size_constraints command without any pre-configured scope.","commands":{"allow":["set_size_constraints"],"deny":[]}},"allow-set-skip-taskbar":{"identifier":"allow-set-skip-taskbar","description":"Enables the set_skip_taskbar command without any pre-configured scope.","commands":{"allow":["set_skip_taskbar"],"deny":[]}},"allow-set-theme":{"identifier":"allow-set-theme","description":"Enables the set_theme command without any pre-configured scope.","commands":{"allow":["set_theme"],"deny":[]}},"allow-set-title":{"identifier":"allow-set-title","description":"Enables the set_title command without any pre-configured scope.","commands":{"allow":["set_title"],"deny":[]}},"allow-set-title-bar-style":{"identifier":"allow-set-title-bar-style","description":"Enables the set_title_bar_style command without any pre-configured scope.","commands":{"allow":["set_title_bar_style"],"deny":[]}},"allow-set-visible-on-all-workspaces":{"identifier":"allow-set-visible-on-all-workspaces","description":"Enables the set_visible_on_all_workspaces command without any pre-configured scope.","commands":{"allow":["set_visible_on_all_workspaces"],"deny":[]}},"allow-show":{"identifier":"allow-show","description":"Enables the show command without any pre-configured scope.","commands":{"allow":["show"],"deny":[]}},"allow-start-dragging":{"identifier":"allow-start-dragging","description":"Enables the start_dragging command without any pre-configured scope.","commands":{"allow":["start_dragging"],"deny":[]}},"allow-start-resize-dragging":{"identifier":"allow-start-resize-dragging","description":"Enables the start_resize_dragging command without any pre-configured scope.","commands":{"allow":["start_resize_dragging"],"deny":[]}},"allow-theme":{"identifier":"allow-theme","description":"Enables the theme command without any pre-configured scope.","commands":{"allow":["theme"],"deny":[]}},"allow-title":{"identifier":"allow-title","description":"Enables the title command without any pre-configured scope.","commands":{"allow":["title"],"deny":[]}},"allow-toggle-maximize":{"identifier":"allow-toggle-maximize","description":"Enables the toggle_maximize command without any pre-configured scope.","commands":{"allow":["toggle_maximize"],"deny":[]}},"allow-unmaximize":{"identifier":"allow-unmaximize","description":"Enables the unmaximize command without any pre-configured scope.","commands":{"allow":["unmaximize"],"deny":[]}},"allow-unminimize":{"identifier":"allow-unminimize","description":"Enables the unminimize command without any pre-configured scope.","commands":{"allow":["unminimize"],"deny":[]}},"deny-available-monitors":{"identifier":"deny-available-monitors","description":"Denies the available_monitors command without any pre-configured scope.","commands":{"allow":[],"deny":["available_monitors"]}},"deny-center":{"identifier":"deny-center","description":"Denies the center command without any pre-configured scope.","commands":{"allow":[],"deny":["center"]}},"deny-close":{"identifier":"deny-close","description":"Denies the close command without any pre-configured scope.","commands":{"allow":[],"deny":["close"]}},"deny-create":{"identifier":"deny-create","description":"Denies the create command without any pre-configured scope.","commands":{"allow":[],"deny":["create"]}},"deny-current-monitor":{"identifier":"deny-current-monitor","description":"Denies the current_monitor command without any pre-configured scope.","commands":{"allow":[],"deny":["current_monitor"]}},"deny-cursor-position":{"identifier":"deny-cursor-position","description":"Denies the cursor_position command without any pre-configured scope.","commands":{"allow":[],"deny":["cursor_position"]}},"deny-destroy":{"identifier":"deny-destroy","description":"Denies the destroy command without any pre-configured scope.","commands":{"allow":[],"deny":["destroy"]}},"deny-get-all-windows":{"identifier":"deny-get-all-windows","description":"Denies the get_all_windows command without any pre-configured scope.","commands":{"allow":[],"deny":["get_all_windows"]}},"deny-hide":{"identifier":"deny-hide","description":"Denies the hide command without any pre-configured scope.","commands":{"allow":[],"deny":["hide"]}},"deny-inner-position":{"identifier":"deny-inner-position","description":"Denies the inner_position command without any pre-configured scope.","commands":{"allow":[],"deny":["inner_position"]}},"deny-inner-size":{"identifier":"deny-inner-size","description":"Denies the inner_size command without any pre-configured scope.","commands":{"allow":[],"deny":["inner_size"]}},"deny-internal-toggle-maximize":{"identifier":"deny-internal-toggle-maximize","description":"Denies the internal_toggle_maximize command without any pre-configured scope.","commands":{"allow":[],"deny":["internal_toggle_maximize"]}},"deny-is-always-on-top":{"identifier":"deny-is-always-on-top","description":"Denies the is_always_on_top command without any pre-configured scope.","commands":{"allow":[],"deny":["is_always_on_top"]}},"deny-is-closable":{"identifier":"deny-is-closable","description":"Denies the is_closable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_closable"]}},"deny-is-decorated":{"identifier":"deny-is-decorated","description":"Denies the is_decorated command without any pre-configured scope.","commands":{"allow":[],"deny":["is_decorated"]}},"deny-is-enabled":{"identifier":"deny-is-enabled","description":"Denies the is_enabled command without any pre-configured scope.","commands":{"allow":[],"deny":["is_enabled"]}},"deny-is-focused":{"identifier":"deny-is-focused","description":"Denies the is_focused command without any pre-configured scope.","commands":{"allow":[],"deny":["is_focused"]}},"deny-is-fullscreen":{"identifier":"deny-is-fullscreen","description":"Denies the is_fullscreen command without any pre-configured scope.","commands":{"allow":[],"deny":["is_fullscreen"]}},"deny-is-maximizable":{"identifier":"deny-is-maximizable","description":"Denies the is_maximizable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_maximizable"]}},"deny-is-maximized":{"identifier":"deny-is-maximized","description":"Denies the is_maximized command without any pre-configured scope.","commands":{"allow":[],"deny":["is_maximized"]}},"deny-is-minimizable":{"identifier":"deny-is-minimizable","description":"Denies the is_minimizable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_minimizable"]}},"deny-is-minimized":{"identifier":"deny-is-minimized","description":"Denies the is_minimized command without any pre-configured scope.","commands":{"allow":[],"deny":["is_minimized"]}},"deny-is-resizable":{"identifier":"deny-is-resizable","description":"Denies the is_resizable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_resizable"]}},"deny-is-visible":{"identifier":"deny-is-visible","description":"Denies the is_visible command without any pre-configured scope.","commands":{"allow":[],"deny":["is_visible"]}},"deny-maximize":{"identifier":"deny-maximize","description":"Denies the maximize command without any pre-configured scope.","commands":{"allow":[],"deny":["maximize"]}},"deny-minimize":{"identifier":"deny-minimize","description":"Denies the minimize command without any pre-configured scope.","commands":{"allow":[],"deny":["minimize"]}},"deny-monitor-from-point":{"identifier":"deny-monitor-from-point","description":"Denies the monitor_from_point command without any pre-configured scope.","commands":{"allow":[],"deny":["monitor_from_point"]}},"deny-outer-position":{"identifier":"deny-outer-position","description":"Denies the outer_position command without any pre-configured scope.","commands":{"allow":[],"deny":["outer_position"]}},"deny-outer-size":{"identifier":"deny-outer-size","description":"Denies the outer_size command without any pre-configured scope.","commands":{"allow":[],"deny":["outer_size"]}},"deny-primary-monitor":{"identifier":"deny-primary-monitor","description":"Denies the primary_monitor command without any pre-configured scope.","commands":{"allow":[],"deny":["primary_monitor"]}},"deny-request-user-attention":{"identifier":"deny-request-user-attention","description":"Denies the request_user_attention command without any pre-configured scope.","commands":{"allow":[],"deny":["request_user_attention"]}},"deny-scale-factor":{"identifier":"deny-scale-factor","description":"Denies the scale_factor command without any pre-configured scope.","commands":{"allow":[],"deny":["scale_factor"]}},"deny-set-always-on-bottom":{"identifier":"deny-set-always-on-bottom","description":"Denies the set_always_on_bottom command without any pre-configured scope.","commands":{"allow":[],"deny":["set_always_on_bottom"]}},"deny-set-always-on-top":{"identifier":"deny-set-always-on-top","description":"Denies the set_always_on_top command without any pre-configured scope.","commands":{"allow":[],"deny":["set_always_on_top"]}},"deny-set-background-color":{"identifier":"deny-set-background-color","description":"Denies the set_background_color command without any pre-configured scope.","commands":{"allow":[],"deny":["set_background_color"]}},"deny-set-badge-count":{"identifier":"deny-set-badge-count","description":"Denies the set_badge_count command without any pre-configured scope.","commands":{"allow":[],"deny":["set_badge_count"]}},"deny-set-badge-label":{"identifier":"deny-set-badge-label","description":"Denies the set_badge_label command without any pre-configured scope.","commands":{"allow":[],"deny":["set_badge_label"]}},"deny-set-closable":{"identifier":"deny-set-closable","description":"Denies the set_closable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_closable"]}},"deny-set-content-protected":{"identifier":"deny-set-content-protected","description":"Denies the set_content_protected command without any pre-configured scope.","commands":{"allow":[],"deny":["set_content_protected"]}},"deny-set-cursor-grab":{"identifier":"deny-set-cursor-grab","description":"Denies the set_cursor_grab command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_grab"]}},"deny-set-cursor-icon":{"identifier":"deny-set-cursor-icon","description":"Denies the set_cursor_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_icon"]}},"deny-set-cursor-position":{"identifier":"deny-set-cursor-position","description":"Denies the set_cursor_position command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_position"]}},"deny-set-cursor-visible":{"identifier":"deny-set-cursor-visible","description":"Denies the set_cursor_visible command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_visible"]}},"deny-set-decorations":{"identifier":"deny-set-decorations","description":"Denies the set_decorations command without any pre-configured scope.","commands":{"allow":[],"deny":["set_decorations"]}},"deny-set-effects":{"identifier":"deny-set-effects","description":"Denies the set_effects command without any pre-configured scope.","commands":{"allow":[],"deny":["set_effects"]}},"deny-set-enabled":{"identifier":"deny-set-enabled","description":"Denies the set_enabled command without any pre-configured scope.","commands":{"allow":[],"deny":["set_enabled"]}},"deny-set-focus":{"identifier":"deny-set-focus","description":"Denies the set_focus command without any pre-configured scope.","commands":{"allow":[],"deny":["set_focus"]}},"deny-set-focusable":{"identifier":"deny-set-focusable","description":"Denies the set_focusable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_focusable"]}},"deny-set-fullscreen":{"identifier":"deny-set-fullscreen","description":"Denies the set_fullscreen command without any pre-configured scope.","commands":{"allow":[],"deny":["set_fullscreen"]}},"deny-set-icon":{"identifier":"deny-set-icon","description":"Denies the set_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon"]}},"deny-set-ignore-cursor-events":{"identifier":"deny-set-ignore-cursor-events","description":"Denies the set_ignore_cursor_events command without any pre-configured scope.","commands":{"allow":[],"deny":["set_ignore_cursor_events"]}},"deny-set-max-size":{"identifier":"deny-set-max-size","description":"Denies the set_max_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_max_size"]}},"deny-set-maximizable":{"identifier":"deny-set-maximizable","description":"Denies the set_maximizable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_maximizable"]}},"deny-set-min-size":{"identifier":"deny-set-min-size","description":"Denies the set_min_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_min_size"]}},"deny-set-minimizable":{"identifier":"deny-set-minimizable","description":"Denies the set_minimizable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_minimizable"]}},"deny-set-overlay-icon":{"identifier":"deny-set-overlay-icon","description":"Denies the set_overlay_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_overlay_icon"]}},"deny-set-position":{"identifier":"deny-set-position","description":"Denies the set_position command without any pre-configured scope.","commands":{"allow":[],"deny":["set_position"]}},"deny-set-progress-bar":{"identifier":"deny-set-progress-bar","description":"Denies the set_progress_bar command without any pre-configured scope.","commands":{"allow":[],"deny":["set_progress_bar"]}},"deny-set-resizable":{"identifier":"deny-set-resizable","description":"Denies the set_resizable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_resizable"]}},"deny-set-shadow":{"identifier":"deny-set-shadow","description":"Denies the set_shadow command without any pre-configured scope.","commands":{"allow":[],"deny":["set_shadow"]}},"deny-set-simple-fullscreen":{"identifier":"deny-set-simple-fullscreen","description":"Denies the set_simple_fullscreen command without any pre-configured scope.","commands":{"allow":[],"deny":["set_simple_fullscreen"]}},"deny-set-size":{"identifier":"deny-set-size","description":"Denies the set_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_size"]}},"deny-set-size-constraints":{"identifier":"deny-set-size-constraints","description":"Denies the set_size_constraints command without any pre-configured scope.","commands":{"allow":[],"deny":["set_size_constraints"]}},"deny-set-skip-taskbar":{"identifier":"deny-set-skip-taskbar","description":"Denies the set_skip_taskbar command without any pre-configured scope.","commands":{"allow":[],"deny":["set_skip_taskbar"]}},"deny-set-theme":{"identifier":"deny-set-theme","description":"Denies the set_theme command without any pre-configured scope.","commands":{"allow":[],"deny":["set_theme"]}},"deny-set-title":{"identifier":"deny-set-title","description":"Denies the set_title command without any pre-configured scope.","commands":{"allow":[],"deny":["set_title"]}},"deny-set-title-bar-style":{"identifier":"deny-set-title-bar-style","description":"Denies the set_title_bar_style command without any pre-configured scope.","commands":{"allow":[],"deny":["set_title_bar_style"]}},"deny-set-visible-on-all-workspaces":{"identifier":"deny-set-visible-on-all-workspaces","description":"Denies the set_visible_on_all_workspaces command without any pre-configured scope.","commands":{"allow":[],"deny":["set_visible_on_all_workspaces"]}},"deny-show":{"identifier":"deny-show","description":"Denies the show command without any pre-configured scope.","commands":{"allow":[],"deny":["show"]}},"deny-start-dragging":{"identifier":"deny-start-dragging","description":"Denies the start_dragging command without any pre-configured scope.","commands":{"allow":[],"deny":["start_dragging"]}},"deny-start-resize-dragging":{"identifier":"deny-start-resize-dragging","description":"Denies the start_resize_dragging command without any pre-configured scope.","commands":{"allow":[],"deny":["start_resize_dragging"]}},"deny-theme":{"identifier":"deny-theme","description":"Denies the theme command without any pre-configured scope.","commands":{"allow":[],"deny":["theme"]}},"deny-title":{"identifier":"deny-title","description":"Denies the title command without any pre-configured scope.","commands":{"allow":[],"deny":["title"]}},"deny-toggle-maximize":{"identifier":"deny-toggle-maximize","description":"Denies the toggle_maximize command without any pre-configured scope.","commands":{"allow":[],"deny":["toggle_maximize"]}},"deny-unmaximize":{"identifier":"deny-unmaximize","description":"Denies the unmaximize command without any pre-configured scope.","commands":{"allow":[],"deny":["unmaximize"]}},"deny-unminimize":{"identifier":"deny-unminimize","description":"Denies the unminimize command without any pre-configured scope.","commands":{"allow":[],"deny":["unminimize"]}}},"permission_sets":{},"global_scope_schema":null},"shell":{"default_permission":{"identifier":"default","description":"This permission set configures which\nshell functionality is exposed by default.\n\n#### Granted Permissions\n\nIt allows to use the `open` functionality with a reasonable\nscope pre-configured. It will allow opening `http(s)://`,\n`tel:` and `mailto:` links.\n","permissions":["allow-open"]},"permissions":{"allow-execute":{"identifier":"allow-execute","description":"Enables the execute command without any pre-configured scope.","commands":{"allow":["execute"],"deny":[]}},"allow-kill":{"identifier":"allow-kill","description":"Enables the kill command without any pre-configured scope.","commands":{"allow":["kill"],"deny":[]}},"allow-open":{"identifier":"allow-open","description":"Enables the open command without any pre-configured scope.","commands":{"allow":["open"],"deny":[]}},"allow-spawn":{"identifier":"allow-spawn","description":"Enables the spawn command without any pre-configured scope.","commands":{"allow":["spawn"],"deny":[]}},"allow-stdin-write":{"identifier":"allow-stdin-write","description":"Enables the stdin_write command without any pre-configured scope.","commands":{"allow":["stdin_write"],"deny":[]}},"deny-execute":{"identifier":"deny-execute","description":"Denies the execute command without any pre-configured scope.","commands":{"allow":[],"deny":["execute"]}},"deny-kill":{"identifier":"deny-kill","description":"Denies the kill command without any pre-configured scope.","commands":{"allow":[],"deny":["kill"]}},"deny-open":{"identifier":"deny-open","description":"Denies the open command without any pre-configured scope.","commands":{"allow":[],"deny":["open"]}},"deny-spawn":{"identifier":"deny-spawn","description":"Denies the spawn command without any pre-configured scope.","commands":{"allow":[],"deny":["spawn"]}},"deny-stdin-write":{"identifier":"deny-stdin-write","description":"Denies the stdin_write command without any pre-configured scope.","commands":{"allow":[],"deny":["stdin_write"]}}},"permission_sets":{},"global_scope_schema":{"$schema":"http://json-schema.org/draft-07/schema#","anyOf":[{"additionalProperties":false,"properties":{"args":{"allOf":[{"$ref":"#/definitions/ShellScopeEntryAllowedArgs"}],"description":"The allowed arguments for the command execution."},"cmd":{"description":"The command name. It can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.","type":"string"},"name":{"description":"The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.","type":"string"}},"required":["cmd","name"],"type":"object"},{"additionalProperties":false,"properties":{"args":{"allOf":[{"$ref":"#/definitions/ShellScopeEntryAllowedArgs"}],"description":"The allowed arguments for the command execution."},"name":{"description":"The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.","type":"string"},"sidecar":{"description":"If this command is a sidecar command.","type":"boolean"}},"required":["name","sidecar"],"type":"object"}],"definitions":{"ShellScopeEntryAllowedArg":{"anyOf":[{"description":"A non-configurable argument that is passed to the command in the order it was specified.","type":"string"},{"additionalProperties":false,"description":"A variable that is set while calling the command from the webview API.","properties":{"raw":{"default":false,"description":"Marks the validator as a raw regex, meaning the plugin should not make any modification at runtime.\n\nThis means the regex will not match on the entire string by default, which might be exploited if your regex allow unexpected input to be considered valid. When using this option, make sure your regex is correct.","type":"boolean"},"validator":{"description":"[regex] validator to require passed values to conform to an expected input.\n\nThis will require the argument value passed to this variable to match the `validator` regex before it will be executed.\n\nThe regex string is by default surrounded by `^...$` to match the full string. For example the `https?://\\w+` regex would be registered as `^https?://\\w+$`.\n\n[regex]: ","type":"string"}},"required":["validator"],"type":"object"}],"description":"A command argument allowed to be executed by the webview API."},"ShellScopeEntryAllowedArgs":{"anyOf":[{"description":"Use a simple boolean to allow all or disable all arguments to this command configuration.","type":"boolean"},{"description":"A specific set of [`ShellScopeEntryAllowedArg`] that are valid to call for the command configuration.","items":{"$ref":"#/definitions/ShellScopeEntryAllowedArg"},"type":"array"}],"description":"A set of command arguments allowed to be executed by the webview API.\n\nA value of `true` will allow any arguments to be passed to the command. `false` will disable all arguments. A list of [`ShellScopeEntryAllowedArg`] will set those arguments as the only valid arguments to be passed to the attached command configuration."}},"description":"Shell scope entry.","title":"ShellScopeEntry"}}}
\ No newline at end of file
+{"core":{"default_permission":{"identifier":"default","description":"Default core plugins set.","permissions":["core:path:default","core:event:default","core:window:default","core:webview:default","core:app:default","core:image:default","core:resources:default","core:menu:default","core:tray:default"]},"permissions":{},"permission_sets":{},"global_scope_schema":null},"core:app":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-version","allow-name","allow-tauri-version","allow-identifier","allow-bundle-type","allow-register-listener","allow-remove-listener","allow-supports-multiple-windows"]},"permissions":{"allow-app-hide":{"identifier":"allow-app-hide","description":"Enables the app_hide command without any pre-configured scope.","commands":{"allow":["app_hide"],"deny":[]}},"allow-app-show":{"identifier":"allow-app-show","description":"Enables the app_show command without any pre-configured scope.","commands":{"allow":["app_show"],"deny":[]}},"allow-bundle-type":{"identifier":"allow-bundle-type","description":"Enables the bundle_type command without any pre-configured scope.","commands":{"allow":["bundle_type"],"deny":[]}},"allow-default-window-icon":{"identifier":"allow-default-window-icon","description":"Enables the default_window_icon command without any pre-configured scope.","commands":{"allow":["default_window_icon"],"deny":[]}},"allow-fetch-data-store-identifiers":{"identifier":"allow-fetch-data-store-identifiers","description":"Enables the fetch_data_store_identifiers command without any pre-configured scope.","commands":{"allow":["fetch_data_store_identifiers"],"deny":[]}},"allow-identifier":{"identifier":"allow-identifier","description":"Enables the identifier command without any pre-configured scope.","commands":{"allow":["identifier"],"deny":[]}},"allow-name":{"identifier":"allow-name","description":"Enables the name command without any pre-configured scope.","commands":{"allow":["name"],"deny":[]}},"allow-register-listener":{"identifier":"allow-register-listener","description":"Enables the register_listener command without any pre-configured scope.","commands":{"allow":["register_listener"],"deny":[]}},"allow-remove-data-store":{"identifier":"allow-remove-data-store","description":"Enables the remove_data_store command without any pre-configured scope.","commands":{"allow":["remove_data_store"],"deny":[]}},"allow-remove-listener":{"identifier":"allow-remove-listener","description":"Enables the remove_listener command without any pre-configured scope.","commands":{"allow":["remove_listener"],"deny":[]}},"allow-set-app-theme":{"identifier":"allow-set-app-theme","description":"Enables the set_app_theme command without any pre-configured scope.","commands":{"allow":["set_app_theme"],"deny":[]}},"allow-set-dock-visibility":{"identifier":"allow-set-dock-visibility","description":"Enables the set_dock_visibility command without any pre-configured scope.","commands":{"allow":["set_dock_visibility"],"deny":[]}},"allow-supports-multiple-windows":{"identifier":"allow-supports-multiple-windows","description":"Enables the supports_multiple_windows command without any pre-configured scope.","commands":{"allow":["supports_multiple_windows"],"deny":[]}},"allow-tauri-version":{"identifier":"allow-tauri-version","description":"Enables the tauri_version command without any pre-configured scope.","commands":{"allow":["tauri_version"],"deny":[]}},"allow-version":{"identifier":"allow-version","description":"Enables the version command without any pre-configured scope.","commands":{"allow":["version"],"deny":[]}},"deny-app-hide":{"identifier":"deny-app-hide","description":"Denies the app_hide command without any pre-configured scope.","commands":{"allow":[],"deny":["app_hide"]}},"deny-app-show":{"identifier":"deny-app-show","description":"Denies the app_show command without any pre-configured scope.","commands":{"allow":[],"deny":["app_show"]}},"deny-bundle-type":{"identifier":"deny-bundle-type","description":"Denies the bundle_type command without any pre-configured scope.","commands":{"allow":[],"deny":["bundle_type"]}},"deny-default-window-icon":{"identifier":"deny-default-window-icon","description":"Denies the default_window_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["default_window_icon"]}},"deny-fetch-data-store-identifiers":{"identifier":"deny-fetch-data-store-identifiers","description":"Denies the fetch_data_store_identifiers command without any pre-configured scope.","commands":{"allow":[],"deny":["fetch_data_store_identifiers"]}},"deny-identifier":{"identifier":"deny-identifier","description":"Denies the identifier command without any pre-configured scope.","commands":{"allow":[],"deny":["identifier"]}},"deny-name":{"identifier":"deny-name","description":"Denies the name command without any pre-configured scope.","commands":{"allow":[],"deny":["name"]}},"deny-register-listener":{"identifier":"deny-register-listener","description":"Denies the register_listener command without any pre-configured scope.","commands":{"allow":[],"deny":["register_listener"]}},"deny-remove-data-store":{"identifier":"deny-remove-data-store","description":"Denies the remove_data_store command without any pre-configured scope.","commands":{"allow":[],"deny":["remove_data_store"]}},"deny-remove-listener":{"identifier":"deny-remove-listener","description":"Denies the remove_listener command without any pre-configured scope.","commands":{"allow":[],"deny":["remove_listener"]}},"deny-set-app-theme":{"identifier":"deny-set-app-theme","description":"Denies the set_app_theme command without any pre-configured scope.","commands":{"allow":[],"deny":["set_app_theme"]}},"deny-set-dock-visibility":{"identifier":"deny-set-dock-visibility","description":"Denies the set_dock_visibility command without any pre-configured scope.","commands":{"allow":[],"deny":["set_dock_visibility"]}},"deny-supports-multiple-windows":{"identifier":"deny-supports-multiple-windows","description":"Denies the supports_multiple_windows command without any pre-configured scope.","commands":{"allow":[],"deny":["supports_multiple_windows"]}},"deny-tauri-version":{"identifier":"deny-tauri-version","description":"Denies the tauri_version command without any pre-configured scope.","commands":{"allow":[],"deny":["tauri_version"]}},"deny-version":{"identifier":"deny-version","description":"Denies the version command without any pre-configured scope.","commands":{"allow":[],"deny":["version"]}}},"permission_sets":{},"global_scope_schema":null},"core:event":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-listen","allow-unlisten","allow-emit","allow-emit-to"]},"permissions":{"allow-emit":{"identifier":"allow-emit","description":"Enables the emit command without any pre-configured scope.","commands":{"allow":["emit"],"deny":[]}},"allow-emit-to":{"identifier":"allow-emit-to","description":"Enables the emit_to command without any pre-configured scope.","commands":{"allow":["emit_to"],"deny":[]}},"allow-listen":{"identifier":"allow-listen","description":"Enables the listen command without any pre-configured scope.","commands":{"allow":["listen"],"deny":[]}},"allow-unlisten":{"identifier":"allow-unlisten","description":"Enables the unlisten command without any pre-configured scope.","commands":{"allow":["unlisten"],"deny":[]}},"deny-emit":{"identifier":"deny-emit","description":"Denies the emit command without any pre-configured scope.","commands":{"allow":[],"deny":["emit"]}},"deny-emit-to":{"identifier":"deny-emit-to","description":"Denies the emit_to command without any pre-configured scope.","commands":{"allow":[],"deny":["emit_to"]}},"deny-listen":{"identifier":"deny-listen","description":"Denies the listen command without any pre-configured scope.","commands":{"allow":[],"deny":["listen"]}},"deny-unlisten":{"identifier":"deny-unlisten","description":"Denies the unlisten command without any pre-configured scope.","commands":{"allow":[],"deny":["unlisten"]}}},"permission_sets":{},"global_scope_schema":null},"core:image":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-new","allow-from-bytes","allow-from-path","allow-rgba","allow-size"]},"permissions":{"allow-from-bytes":{"identifier":"allow-from-bytes","description":"Enables the from_bytes command without any pre-configured scope.","commands":{"allow":["from_bytes"],"deny":[]}},"allow-from-path":{"identifier":"allow-from-path","description":"Enables the from_path command without any pre-configured scope.","commands":{"allow":["from_path"],"deny":[]}},"allow-new":{"identifier":"allow-new","description":"Enables the new command without any pre-configured scope.","commands":{"allow":["new"],"deny":[]}},"allow-rgba":{"identifier":"allow-rgba","description":"Enables the rgba command without any pre-configured scope.","commands":{"allow":["rgba"],"deny":[]}},"allow-size":{"identifier":"allow-size","description":"Enables the size command without any pre-configured scope.","commands":{"allow":["size"],"deny":[]}},"deny-from-bytes":{"identifier":"deny-from-bytes","description":"Denies the from_bytes command without any pre-configured scope.","commands":{"allow":[],"deny":["from_bytes"]}},"deny-from-path":{"identifier":"deny-from-path","description":"Denies the from_path command without any pre-configured scope.","commands":{"allow":[],"deny":["from_path"]}},"deny-new":{"identifier":"deny-new","description":"Denies the new command without any pre-configured scope.","commands":{"allow":[],"deny":["new"]}},"deny-rgba":{"identifier":"deny-rgba","description":"Denies the rgba command without any pre-configured scope.","commands":{"allow":[],"deny":["rgba"]}},"deny-size":{"identifier":"deny-size","description":"Denies the size command without any pre-configured scope.","commands":{"allow":[],"deny":["size"]}}},"permission_sets":{},"global_scope_schema":null},"core:menu":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-new","allow-append","allow-prepend","allow-insert","allow-remove","allow-remove-at","allow-items","allow-get","allow-popup","allow-create-default","allow-set-as-app-menu","allow-set-as-window-menu","allow-text","allow-set-text","allow-is-enabled","allow-set-enabled","allow-set-accelerator","allow-set-as-windows-menu-for-nsapp","allow-set-as-help-menu-for-nsapp","allow-is-checked","allow-set-checked","allow-set-icon"]},"permissions":{"allow-append":{"identifier":"allow-append","description":"Enables the append command without any pre-configured scope.","commands":{"allow":["append"],"deny":[]}},"allow-create-default":{"identifier":"allow-create-default","description":"Enables the create_default command without any pre-configured scope.","commands":{"allow":["create_default"],"deny":[]}},"allow-get":{"identifier":"allow-get","description":"Enables the get command without any pre-configured scope.","commands":{"allow":["get"],"deny":[]}},"allow-insert":{"identifier":"allow-insert","description":"Enables the insert command without any pre-configured scope.","commands":{"allow":["insert"],"deny":[]}},"allow-is-checked":{"identifier":"allow-is-checked","description":"Enables the is_checked command without any pre-configured scope.","commands":{"allow":["is_checked"],"deny":[]}},"allow-is-enabled":{"identifier":"allow-is-enabled","description":"Enables the is_enabled command without any pre-configured scope.","commands":{"allow":["is_enabled"],"deny":[]}},"allow-items":{"identifier":"allow-items","description":"Enables the items command without any pre-configured scope.","commands":{"allow":["items"],"deny":[]}},"allow-new":{"identifier":"allow-new","description":"Enables the new command without any pre-configured scope.","commands":{"allow":["new"],"deny":[]}},"allow-popup":{"identifier":"allow-popup","description":"Enables the popup command without any pre-configured scope.","commands":{"allow":["popup"],"deny":[]}},"allow-prepend":{"identifier":"allow-prepend","description":"Enables the prepend command without any pre-configured scope.","commands":{"allow":["prepend"],"deny":[]}},"allow-remove":{"identifier":"allow-remove","description":"Enables the remove command without any pre-configured scope.","commands":{"allow":["remove"],"deny":[]}},"allow-remove-at":{"identifier":"allow-remove-at","description":"Enables the remove_at command without any pre-configured scope.","commands":{"allow":["remove_at"],"deny":[]}},"allow-set-accelerator":{"identifier":"allow-set-accelerator","description":"Enables the set_accelerator command without any pre-configured scope.","commands":{"allow":["set_accelerator"],"deny":[]}},"allow-set-as-app-menu":{"identifier":"allow-set-as-app-menu","description":"Enables the set_as_app_menu command without any pre-configured scope.","commands":{"allow":["set_as_app_menu"],"deny":[]}},"allow-set-as-help-menu-for-nsapp":{"identifier":"allow-set-as-help-menu-for-nsapp","description":"Enables the set_as_help_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":["set_as_help_menu_for_nsapp"],"deny":[]}},"allow-set-as-window-menu":{"identifier":"allow-set-as-window-menu","description":"Enables the set_as_window_menu command without any pre-configured scope.","commands":{"allow":["set_as_window_menu"],"deny":[]}},"allow-set-as-windows-menu-for-nsapp":{"identifier":"allow-set-as-windows-menu-for-nsapp","description":"Enables the set_as_windows_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":["set_as_windows_menu_for_nsapp"],"deny":[]}},"allow-set-checked":{"identifier":"allow-set-checked","description":"Enables the set_checked command without any pre-configured scope.","commands":{"allow":["set_checked"],"deny":[]}},"allow-set-enabled":{"identifier":"allow-set-enabled","description":"Enables the set_enabled command without any pre-configured scope.","commands":{"allow":["set_enabled"],"deny":[]}},"allow-set-icon":{"identifier":"allow-set-icon","description":"Enables the set_icon command without any pre-configured scope.","commands":{"allow":["set_icon"],"deny":[]}},"allow-set-text":{"identifier":"allow-set-text","description":"Enables the set_text command without any pre-configured scope.","commands":{"allow":["set_text"],"deny":[]}},"allow-text":{"identifier":"allow-text","description":"Enables the text command without any pre-configured scope.","commands":{"allow":["text"],"deny":[]}},"deny-append":{"identifier":"deny-append","description":"Denies the append command without any pre-configured scope.","commands":{"allow":[],"deny":["append"]}},"deny-create-default":{"identifier":"deny-create-default","description":"Denies the create_default command without any pre-configured scope.","commands":{"allow":[],"deny":["create_default"]}},"deny-get":{"identifier":"deny-get","description":"Denies the get command without any pre-configured scope.","commands":{"allow":[],"deny":["get"]}},"deny-insert":{"identifier":"deny-insert","description":"Denies the insert command without any pre-configured scope.","commands":{"allow":[],"deny":["insert"]}},"deny-is-checked":{"identifier":"deny-is-checked","description":"Denies the is_checked command without any pre-configured scope.","commands":{"allow":[],"deny":["is_checked"]}},"deny-is-enabled":{"identifier":"deny-is-enabled","description":"Denies the is_enabled command without any pre-configured scope.","commands":{"allow":[],"deny":["is_enabled"]}},"deny-items":{"identifier":"deny-items","description":"Denies the items command without any pre-configured scope.","commands":{"allow":[],"deny":["items"]}},"deny-new":{"identifier":"deny-new","description":"Denies the new command without any pre-configured scope.","commands":{"allow":[],"deny":["new"]}},"deny-popup":{"identifier":"deny-popup","description":"Denies the popup command without any pre-configured scope.","commands":{"allow":[],"deny":["popup"]}},"deny-prepend":{"identifier":"deny-prepend","description":"Denies the prepend command without any pre-configured scope.","commands":{"allow":[],"deny":["prepend"]}},"deny-remove":{"identifier":"deny-remove","description":"Denies the remove command without any pre-configured scope.","commands":{"allow":[],"deny":["remove"]}},"deny-remove-at":{"identifier":"deny-remove-at","description":"Denies the remove_at command without any pre-configured scope.","commands":{"allow":[],"deny":["remove_at"]}},"deny-set-accelerator":{"identifier":"deny-set-accelerator","description":"Denies the set_accelerator command without any pre-configured scope.","commands":{"allow":[],"deny":["set_accelerator"]}},"deny-set-as-app-menu":{"identifier":"deny-set-as-app-menu","description":"Denies the set_as_app_menu command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_app_menu"]}},"deny-set-as-help-menu-for-nsapp":{"identifier":"deny-set-as-help-menu-for-nsapp","description":"Denies the set_as_help_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_help_menu_for_nsapp"]}},"deny-set-as-window-menu":{"identifier":"deny-set-as-window-menu","description":"Denies the set_as_window_menu command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_window_menu"]}},"deny-set-as-windows-menu-for-nsapp":{"identifier":"deny-set-as-windows-menu-for-nsapp","description":"Denies the set_as_windows_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_windows_menu_for_nsapp"]}},"deny-set-checked":{"identifier":"deny-set-checked","description":"Denies the set_checked command without any pre-configured scope.","commands":{"allow":[],"deny":["set_checked"]}},"deny-set-enabled":{"identifier":"deny-set-enabled","description":"Denies the set_enabled command without any pre-configured scope.","commands":{"allow":[],"deny":["set_enabled"]}},"deny-set-icon":{"identifier":"deny-set-icon","description":"Denies the set_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon"]}},"deny-set-text":{"identifier":"deny-set-text","description":"Denies the set_text command without any pre-configured scope.","commands":{"allow":[],"deny":["set_text"]}},"deny-text":{"identifier":"deny-text","description":"Denies the text command without any pre-configured scope.","commands":{"allow":[],"deny":["text"]}}},"permission_sets":{},"global_scope_schema":null},"core:path":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-resolve-directory","allow-resolve","allow-normalize","allow-join","allow-dirname","allow-extname","allow-basename","allow-is-absolute"]},"permissions":{"allow-basename":{"identifier":"allow-basename","description":"Enables the basename command without any pre-configured scope.","commands":{"allow":["basename"],"deny":[]}},"allow-dirname":{"identifier":"allow-dirname","description":"Enables the dirname command without any pre-configured scope.","commands":{"allow":["dirname"],"deny":[]}},"allow-extname":{"identifier":"allow-extname","description":"Enables the extname command without any pre-configured scope.","commands":{"allow":["extname"],"deny":[]}},"allow-is-absolute":{"identifier":"allow-is-absolute","description":"Enables the is_absolute command without any pre-configured scope.","commands":{"allow":["is_absolute"],"deny":[]}},"allow-join":{"identifier":"allow-join","description":"Enables the join command without any pre-configured scope.","commands":{"allow":["join"],"deny":[]}},"allow-normalize":{"identifier":"allow-normalize","description":"Enables the normalize command without any pre-configured scope.","commands":{"allow":["normalize"],"deny":[]}},"allow-resolve":{"identifier":"allow-resolve","description":"Enables the resolve command without any pre-configured scope.","commands":{"allow":["resolve"],"deny":[]}},"allow-resolve-directory":{"identifier":"allow-resolve-directory","description":"Enables the resolve_directory command without any pre-configured scope.","commands":{"allow":["resolve_directory"],"deny":[]}},"deny-basename":{"identifier":"deny-basename","description":"Denies the basename command without any pre-configured scope.","commands":{"allow":[],"deny":["basename"]}},"deny-dirname":{"identifier":"deny-dirname","description":"Denies the dirname command without any pre-configured scope.","commands":{"allow":[],"deny":["dirname"]}},"deny-extname":{"identifier":"deny-extname","description":"Denies the extname command without any pre-configured scope.","commands":{"allow":[],"deny":["extname"]}},"deny-is-absolute":{"identifier":"deny-is-absolute","description":"Denies the is_absolute command without any pre-configured scope.","commands":{"allow":[],"deny":["is_absolute"]}},"deny-join":{"identifier":"deny-join","description":"Denies the join command without any pre-configured scope.","commands":{"allow":[],"deny":["join"]}},"deny-normalize":{"identifier":"deny-normalize","description":"Denies the normalize command without any pre-configured scope.","commands":{"allow":[],"deny":["normalize"]}},"deny-resolve":{"identifier":"deny-resolve","description":"Denies the resolve command without any pre-configured scope.","commands":{"allow":[],"deny":["resolve"]}},"deny-resolve-directory":{"identifier":"deny-resolve-directory","description":"Denies the resolve_directory command without any pre-configured scope.","commands":{"allow":[],"deny":["resolve_directory"]}}},"permission_sets":{},"global_scope_schema":null},"core:resources":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-close"]},"permissions":{"allow-close":{"identifier":"allow-close","description":"Enables the close command without any pre-configured scope.","commands":{"allow":["close"],"deny":[]}},"deny-close":{"identifier":"deny-close","description":"Denies the close command without any pre-configured scope.","commands":{"allow":[],"deny":["close"]}}},"permission_sets":{},"global_scope_schema":null},"core:tray":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-new","allow-get-by-id","allow-remove-by-id","allow-set-icon","allow-set-menu","allow-set-tooltip","allow-set-title","allow-set-visible","allow-set-temp-dir-path","allow-set-icon-as-template","allow-set-icon-with-as-template","allow-set-show-menu-on-left-click"]},"permissions":{"allow-get-by-id":{"identifier":"allow-get-by-id","description":"Enables the get_by_id command without any pre-configured scope.","commands":{"allow":["get_by_id"],"deny":[]}},"allow-new":{"identifier":"allow-new","description":"Enables the new command without any pre-configured scope.","commands":{"allow":["new"],"deny":[]}},"allow-remove-by-id":{"identifier":"allow-remove-by-id","description":"Enables the remove_by_id command without any pre-configured scope.","commands":{"allow":["remove_by_id"],"deny":[]}},"allow-set-icon":{"identifier":"allow-set-icon","description":"Enables the set_icon command without any pre-configured scope.","commands":{"allow":["set_icon"],"deny":[]}},"allow-set-icon-as-template":{"identifier":"allow-set-icon-as-template","description":"Enables the set_icon_as_template command without any pre-configured scope.","commands":{"allow":["set_icon_as_template"],"deny":[]}},"allow-set-icon-with-as-template":{"identifier":"allow-set-icon-with-as-template","description":"Enables the set_icon_with_as_template command without any pre-configured scope.","commands":{"allow":["set_icon_with_as_template"],"deny":[]}},"allow-set-menu":{"identifier":"allow-set-menu","description":"Enables the set_menu command without any pre-configured scope.","commands":{"allow":["set_menu"],"deny":[]}},"allow-set-show-menu-on-left-click":{"identifier":"allow-set-show-menu-on-left-click","description":"Enables the set_show_menu_on_left_click command without any pre-configured scope.","commands":{"allow":["set_show_menu_on_left_click"],"deny":[]}},"allow-set-temp-dir-path":{"identifier":"allow-set-temp-dir-path","description":"Enables the set_temp_dir_path command without any pre-configured scope.","commands":{"allow":["set_temp_dir_path"],"deny":[]}},"allow-set-title":{"identifier":"allow-set-title","description":"Enables the set_title command without any pre-configured scope.","commands":{"allow":["set_title"],"deny":[]}},"allow-set-tooltip":{"identifier":"allow-set-tooltip","description":"Enables the set_tooltip command without any pre-configured scope.","commands":{"allow":["set_tooltip"],"deny":[]}},"allow-set-visible":{"identifier":"allow-set-visible","description":"Enables the set_visible command without any pre-configured scope.","commands":{"allow":["set_visible"],"deny":[]}},"deny-get-by-id":{"identifier":"deny-get-by-id","description":"Denies the get_by_id command without any pre-configured scope.","commands":{"allow":[],"deny":["get_by_id"]}},"deny-new":{"identifier":"deny-new","description":"Denies the new command without any pre-configured scope.","commands":{"allow":[],"deny":["new"]}},"deny-remove-by-id":{"identifier":"deny-remove-by-id","description":"Denies the remove_by_id command without any pre-configured scope.","commands":{"allow":[],"deny":["remove_by_id"]}},"deny-set-icon":{"identifier":"deny-set-icon","description":"Denies the set_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon"]}},"deny-set-icon-as-template":{"identifier":"deny-set-icon-as-template","description":"Denies the set_icon_as_template command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon_as_template"]}},"deny-set-icon-with-as-template":{"identifier":"deny-set-icon-with-as-template","description":"Denies the set_icon_with_as_template command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon_with_as_template"]}},"deny-set-menu":{"identifier":"deny-set-menu","description":"Denies the set_menu command without any pre-configured scope.","commands":{"allow":[],"deny":["set_menu"]}},"deny-set-show-menu-on-left-click":{"identifier":"deny-set-show-menu-on-left-click","description":"Denies the set_show_menu_on_left_click command without any pre-configured scope.","commands":{"allow":[],"deny":["set_show_menu_on_left_click"]}},"deny-set-temp-dir-path":{"identifier":"deny-set-temp-dir-path","description":"Denies the set_temp_dir_path command without any pre-configured scope.","commands":{"allow":[],"deny":["set_temp_dir_path"]}},"deny-set-title":{"identifier":"deny-set-title","description":"Denies the set_title command without any pre-configured scope.","commands":{"allow":[],"deny":["set_title"]}},"deny-set-tooltip":{"identifier":"deny-set-tooltip","description":"Denies the set_tooltip command without any pre-configured scope.","commands":{"allow":[],"deny":["set_tooltip"]}},"deny-set-visible":{"identifier":"deny-set-visible","description":"Denies the set_visible command without any pre-configured scope.","commands":{"allow":[],"deny":["set_visible"]}}},"permission_sets":{},"global_scope_schema":null},"core:webview":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-get-all-webviews","allow-webview-position","allow-webview-size","allow-internal-toggle-devtools"]},"permissions":{"allow-clear-all-browsing-data":{"identifier":"allow-clear-all-browsing-data","description":"Enables the clear_all_browsing_data command without any pre-configured scope.","commands":{"allow":["clear_all_browsing_data"],"deny":[]}},"allow-create-webview":{"identifier":"allow-create-webview","description":"Enables the create_webview command without any pre-configured scope.","commands":{"allow":["create_webview"],"deny":[]}},"allow-create-webview-window":{"identifier":"allow-create-webview-window","description":"Enables the create_webview_window command without any pre-configured scope.","commands":{"allow":["create_webview_window"],"deny":[]}},"allow-get-all-webviews":{"identifier":"allow-get-all-webviews","description":"Enables the get_all_webviews command without any pre-configured scope.","commands":{"allow":["get_all_webviews"],"deny":[]}},"allow-internal-toggle-devtools":{"identifier":"allow-internal-toggle-devtools","description":"Enables the internal_toggle_devtools command without any pre-configured scope.","commands":{"allow":["internal_toggle_devtools"],"deny":[]}},"allow-print":{"identifier":"allow-print","description":"Enables the print command without any pre-configured scope.","commands":{"allow":["print"],"deny":[]}},"allow-reparent":{"identifier":"allow-reparent","description":"Enables the reparent command without any pre-configured scope.","commands":{"allow":["reparent"],"deny":[]}},"allow-set-webview-auto-resize":{"identifier":"allow-set-webview-auto-resize","description":"Enables the set_webview_auto_resize command without any pre-configured scope.","commands":{"allow":["set_webview_auto_resize"],"deny":[]}},"allow-set-webview-background-color":{"identifier":"allow-set-webview-background-color","description":"Enables the set_webview_background_color command without any pre-configured scope.","commands":{"allow":["set_webview_background_color"],"deny":[]}},"allow-set-webview-focus":{"identifier":"allow-set-webview-focus","description":"Enables the set_webview_focus command without any pre-configured scope.","commands":{"allow":["set_webview_focus"],"deny":[]}},"allow-set-webview-position":{"identifier":"allow-set-webview-position","description":"Enables the set_webview_position command without any pre-configured scope.","commands":{"allow":["set_webview_position"],"deny":[]}},"allow-set-webview-size":{"identifier":"allow-set-webview-size","description":"Enables the set_webview_size command without any pre-configured scope.","commands":{"allow":["set_webview_size"],"deny":[]}},"allow-set-webview-zoom":{"identifier":"allow-set-webview-zoom","description":"Enables the set_webview_zoom command without any pre-configured scope.","commands":{"allow":["set_webview_zoom"],"deny":[]}},"allow-webview-close":{"identifier":"allow-webview-close","description":"Enables the webview_close command without any pre-configured scope.","commands":{"allow":["webview_close"],"deny":[]}},"allow-webview-hide":{"identifier":"allow-webview-hide","description":"Enables the webview_hide command without any pre-configured scope.","commands":{"allow":["webview_hide"],"deny":[]}},"allow-webview-position":{"identifier":"allow-webview-position","description":"Enables the webview_position command without any pre-configured scope.","commands":{"allow":["webview_position"],"deny":[]}},"allow-webview-show":{"identifier":"allow-webview-show","description":"Enables the webview_show command without any pre-configured scope.","commands":{"allow":["webview_show"],"deny":[]}},"allow-webview-size":{"identifier":"allow-webview-size","description":"Enables the webview_size command without any pre-configured scope.","commands":{"allow":["webview_size"],"deny":[]}},"deny-clear-all-browsing-data":{"identifier":"deny-clear-all-browsing-data","description":"Denies the clear_all_browsing_data command without any pre-configured scope.","commands":{"allow":[],"deny":["clear_all_browsing_data"]}},"deny-create-webview":{"identifier":"deny-create-webview","description":"Denies the create_webview command without any pre-configured scope.","commands":{"allow":[],"deny":["create_webview"]}},"deny-create-webview-window":{"identifier":"deny-create-webview-window","description":"Denies the create_webview_window command without any pre-configured scope.","commands":{"allow":[],"deny":["create_webview_window"]}},"deny-get-all-webviews":{"identifier":"deny-get-all-webviews","description":"Denies the get_all_webviews command without any pre-configured scope.","commands":{"allow":[],"deny":["get_all_webviews"]}},"deny-internal-toggle-devtools":{"identifier":"deny-internal-toggle-devtools","description":"Denies the internal_toggle_devtools command without any pre-configured scope.","commands":{"allow":[],"deny":["internal_toggle_devtools"]}},"deny-print":{"identifier":"deny-print","description":"Denies the print command without any pre-configured scope.","commands":{"allow":[],"deny":["print"]}},"deny-reparent":{"identifier":"deny-reparent","description":"Denies the reparent command without any pre-configured scope.","commands":{"allow":[],"deny":["reparent"]}},"deny-set-webview-auto-resize":{"identifier":"deny-set-webview-auto-resize","description":"Denies the set_webview_auto_resize command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_auto_resize"]}},"deny-set-webview-background-color":{"identifier":"deny-set-webview-background-color","description":"Denies the set_webview_background_color command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_background_color"]}},"deny-set-webview-focus":{"identifier":"deny-set-webview-focus","description":"Denies the set_webview_focus command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_focus"]}},"deny-set-webview-position":{"identifier":"deny-set-webview-position","description":"Denies the set_webview_position command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_position"]}},"deny-set-webview-size":{"identifier":"deny-set-webview-size","description":"Denies the set_webview_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_size"]}},"deny-set-webview-zoom":{"identifier":"deny-set-webview-zoom","description":"Denies the set_webview_zoom command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_zoom"]}},"deny-webview-close":{"identifier":"deny-webview-close","description":"Denies the webview_close command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_close"]}},"deny-webview-hide":{"identifier":"deny-webview-hide","description":"Denies the webview_hide command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_hide"]}},"deny-webview-position":{"identifier":"deny-webview-position","description":"Denies the webview_position command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_position"]}},"deny-webview-show":{"identifier":"deny-webview-show","description":"Denies the webview_show command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_show"]}},"deny-webview-size":{"identifier":"deny-webview-size","description":"Denies the webview_size command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_size"]}}},"permission_sets":{},"global_scope_schema":null},"core:window":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-get-all-windows","allow-scale-factor","allow-inner-position","allow-outer-position","allow-inner-size","allow-outer-size","allow-is-fullscreen","allow-is-minimized","allow-is-maximized","allow-is-focused","allow-is-decorated","allow-is-resizable","allow-is-maximizable","allow-is-minimizable","allow-is-closable","allow-is-visible","allow-is-enabled","allow-title","allow-current-monitor","allow-primary-monitor","allow-monitor-from-point","allow-available-monitors","allow-cursor-position","allow-theme","allow-is-always-on-top","allow-activity-name","allow-scene-identifier","allow-internal-toggle-maximize"]},"permissions":{"allow-activity-name":{"identifier":"allow-activity-name","description":"Enables the activity_name command without any pre-configured scope.","commands":{"allow":["activity_name"],"deny":[]}},"allow-available-monitors":{"identifier":"allow-available-monitors","description":"Enables the available_monitors command without any pre-configured scope.","commands":{"allow":["available_monitors"],"deny":[]}},"allow-center":{"identifier":"allow-center","description":"Enables the center command without any pre-configured scope.","commands":{"allow":["center"],"deny":[]}},"allow-close":{"identifier":"allow-close","description":"Enables the close command without any pre-configured scope.","commands":{"allow":["close"],"deny":[]}},"allow-create":{"identifier":"allow-create","description":"Enables the create command without any pre-configured scope.","commands":{"allow":["create"],"deny":[]}},"allow-current-monitor":{"identifier":"allow-current-monitor","description":"Enables the current_monitor command without any pre-configured scope.","commands":{"allow":["current_monitor"],"deny":[]}},"allow-cursor-position":{"identifier":"allow-cursor-position","description":"Enables the cursor_position command without any pre-configured scope.","commands":{"allow":["cursor_position"],"deny":[]}},"allow-destroy":{"identifier":"allow-destroy","description":"Enables the destroy command without any pre-configured scope.","commands":{"allow":["destroy"],"deny":[]}},"allow-get-all-windows":{"identifier":"allow-get-all-windows","description":"Enables the get_all_windows command without any pre-configured scope.","commands":{"allow":["get_all_windows"],"deny":[]}},"allow-hide":{"identifier":"allow-hide","description":"Enables the hide command without any pre-configured scope.","commands":{"allow":["hide"],"deny":[]}},"allow-inner-position":{"identifier":"allow-inner-position","description":"Enables the inner_position command without any pre-configured scope.","commands":{"allow":["inner_position"],"deny":[]}},"allow-inner-size":{"identifier":"allow-inner-size","description":"Enables the inner_size command without any pre-configured scope.","commands":{"allow":["inner_size"],"deny":[]}},"allow-internal-toggle-maximize":{"identifier":"allow-internal-toggle-maximize","description":"Enables the internal_toggle_maximize command without any pre-configured scope.","commands":{"allow":["internal_toggle_maximize"],"deny":[]}},"allow-is-always-on-top":{"identifier":"allow-is-always-on-top","description":"Enables the is_always_on_top command without any pre-configured scope.","commands":{"allow":["is_always_on_top"],"deny":[]}},"allow-is-closable":{"identifier":"allow-is-closable","description":"Enables the is_closable command without any pre-configured scope.","commands":{"allow":["is_closable"],"deny":[]}},"allow-is-decorated":{"identifier":"allow-is-decorated","description":"Enables the is_decorated command without any pre-configured scope.","commands":{"allow":["is_decorated"],"deny":[]}},"allow-is-enabled":{"identifier":"allow-is-enabled","description":"Enables the is_enabled command without any pre-configured scope.","commands":{"allow":["is_enabled"],"deny":[]}},"allow-is-focused":{"identifier":"allow-is-focused","description":"Enables the is_focused command without any pre-configured scope.","commands":{"allow":["is_focused"],"deny":[]}},"allow-is-fullscreen":{"identifier":"allow-is-fullscreen","description":"Enables the is_fullscreen command without any pre-configured scope.","commands":{"allow":["is_fullscreen"],"deny":[]}},"allow-is-maximizable":{"identifier":"allow-is-maximizable","description":"Enables the is_maximizable command without any pre-configured scope.","commands":{"allow":["is_maximizable"],"deny":[]}},"allow-is-maximized":{"identifier":"allow-is-maximized","description":"Enables the is_maximized command without any pre-configured scope.","commands":{"allow":["is_maximized"],"deny":[]}},"allow-is-minimizable":{"identifier":"allow-is-minimizable","description":"Enables the is_minimizable command without any pre-configured scope.","commands":{"allow":["is_minimizable"],"deny":[]}},"allow-is-minimized":{"identifier":"allow-is-minimized","description":"Enables the is_minimized command without any pre-configured scope.","commands":{"allow":["is_minimized"],"deny":[]}},"allow-is-resizable":{"identifier":"allow-is-resizable","description":"Enables the is_resizable command without any pre-configured scope.","commands":{"allow":["is_resizable"],"deny":[]}},"allow-is-visible":{"identifier":"allow-is-visible","description":"Enables the is_visible command without any pre-configured scope.","commands":{"allow":["is_visible"],"deny":[]}},"allow-maximize":{"identifier":"allow-maximize","description":"Enables the maximize command without any pre-configured scope.","commands":{"allow":["maximize"],"deny":[]}},"allow-minimize":{"identifier":"allow-minimize","description":"Enables the minimize command without any pre-configured scope.","commands":{"allow":["minimize"],"deny":[]}},"allow-monitor-from-point":{"identifier":"allow-monitor-from-point","description":"Enables the monitor_from_point command without any pre-configured scope.","commands":{"allow":["monitor_from_point"],"deny":[]}},"allow-outer-position":{"identifier":"allow-outer-position","description":"Enables the outer_position command without any pre-configured scope.","commands":{"allow":["outer_position"],"deny":[]}},"allow-outer-size":{"identifier":"allow-outer-size","description":"Enables the outer_size command without any pre-configured scope.","commands":{"allow":["outer_size"],"deny":[]}},"allow-primary-monitor":{"identifier":"allow-primary-monitor","description":"Enables the primary_monitor command without any pre-configured scope.","commands":{"allow":["primary_monitor"],"deny":[]}},"allow-request-user-attention":{"identifier":"allow-request-user-attention","description":"Enables the request_user_attention command without any pre-configured scope.","commands":{"allow":["request_user_attention"],"deny":[]}},"allow-scale-factor":{"identifier":"allow-scale-factor","description":"Enables the scale_factor command without any pre-configured scope.","commands":{"allow":["scale_factor"],"deny":[]}},"allow-scene-identifier":{"identifier":"allow-scene-identifier","description":"Enables the scene_identifier command without any pre-configured scope.","commands":{"allow":["scene_identifier"],"deny":[]}},"allow-set-always-on-bottom":{"identifier":"allow-set-always-on-bottom","description":"Enables the set_always_on_bottom command without any pre-configured scope.","commands":{"allow":["set_always_on_bottom"],"deny":[]}},"allow-set-always-on-top":{"identifier":"allow-set-always-on-top","description":"Enables the set_always_on_top command without any pre-configured scope.","commands":{"allow":["set_always_on_top"],"deny":[]}},"allow-set-background-color":{"identifier":"allow-set-background-color","description":"Enables the set_background_color command without any pre-configured scope.","commands":{"allow":["set_background_color"],"deny":[]}},"allow-set-badge-count":{"identifier":"allow-set-badge-count","description":"Enables the set_badge_count command without any pre-configured scope.","commands":{"allow":["set_badge_count"],"deny":[]}},"allow-set-badge-label":{"identifier":"allow-set-badge-label","description":"Enables the set_badge_label command without any pre-configured scope.","commands":{"allow":["set_badge_label"],"deny":[]}},"allow-set-closable":{"identifier":"allow-set-closable","description":"Enables the set_closable command without any pre-configured scope.","commands":{"allow":["set_closable"],"deny":[]}},"allow-set-content-protected":{"identifier":"allow-set-content-protected","description":"Enables the set_content_protected command without any pre-configured scope.","commands":{"allow":["set_content_protected"],"deny":[]}},"allow-set-cursor-grab":{"identifier":"allow-set-cursor-grab","description":"Enables the set_cursor_grab command without any pre-configured scope.","commands":{"allow":["set_cursor_grab"],"deny":[]}},"allow-set-cursor-icon":{"identifier":"allow-set-cursor-icon","description":"Enables the set_cursor_icon command without any pre-configured scope.","commands":{"allow":["set_cursor_icon"],"deny":[]}},"allow-set-cursor-position":{"identifier":"allow-set-cursor-position","description":"Enables the set_cursor_position command without any pre-configured scope.","commands":{"allow":["set_cursor_position"],"deny":[]}},"allow-set-cursor-visible":{"identifier":"allow-set-cursor-visible","description":"Enables the set_cursor_visible command without any pre-configured scope.","commands":{"allow":["set_cursor_visible"],"deny":[]}},"allow-set-decorations":{"identifier":"allow-set-decorations","description":"Enables the set_decorations command without any pre-configured scope.","commands":{"allow":["set_decorations"],"deny":[]}},"allow-set-effects":{"identifier":"allow-set-effects","description":"Enables the set_effects command without any pre-configured scope.","commands":{"allow":["set_effects"],"deny":[]}},"allow-set-enabled":{"identifier":"allow-set-enabled","description":"Enables the set_enabled command without any pre-configured scope.","commands":{"allow":["set_enabled"],"deny":[]}},"allow-set-focus":{"identifier":"allow-set-focus","description":"Enables the set_focus command without any pre-configured scope.","commands":{"allow":["set_focus"],"deny":[]}},"allow-set-focusable":{"identifier":"allow-set-focusable","description":"Enables the set_focusable command without any pre-configured scope.","commands":{"allow":["set_focusable"],"deny":[]}},"allow-set-fullscreen":{"identifier":"allow-set-fullscreen","description":"Enables the set_fullscreen command without any pre-configured scope.","commands":{"allow":["set_fullscreen"],"deny":[]}},"allow-set-icon":{"identifier":"allow-set-icon","description":"Enables the set_icon command without any pre-configured scope.","commands":{"allow":["set_icon"],"deny":[]}},"allow-set-ignore-cursor-events":{"identifier":"allow-set-ignore-cursor-events","description":"Enables the set_ignore_cursor_events command without any pre-configured scope.","commands":{"allow":["set_ignore_cursor_events"],"deny":[]}},"allow-set-max-size":{"identifier":"allow-set-max-size","description":"Enables the set_max_size command without any pre-configured scope.","commands":{"allow":["set_max_size"],"deny":[]}},"allow-set-maximizable":{"identifier":"allow-set-maximizable","description":"Enables the set_maximizable command without any pre-configured scope.","commands":{"allow":["set_maximizable"],"deny":[]}},"allow-set-min-size":{"identifier":"allow-set-min-size","description":"Enables the set_min_size command without any pre-configured scope.","commands":{"allow":["set_min_size"],"deny":[]}},"allow-set-minimizable":{"identifier":"allow-set-minimizable","description":"Enables the set_minimizable command without any pre-configured scope.","commands":{"allow":["set_minimizable"],"deny":[]}},"allow-set-overlay-icon":{"identifier":"allow-set-overlay-icon","description":"Enables the set_overlay_icon command without any pre-configured scope.","commands":{"allow":["set_overlay_icon"],"deny":[]}},"allow-set-position":{"identifier":"allow-set-position","description":"Enables the set_position command without any pre-configured scope.","commands":{"allow":["set_position"],"deny":[]}},"allow-set-progress-bar":{"identifier":"allow-set-progress-bar","description":"Enables the set_progress_bar command without any pre-configured scope.","commands":{"allow":["set_progress_bar"],"deny":[]}},"allow-set-resizable":{"identifier":"allow-set-resizable","description":"Enables the set_resizable command without any pre-configured scope.","commands":{"allow":["set_resizable"],"deny":[]}},"allow-set-shadow":{"identifier":"allow-set-shadow","description":"Enables the set_shadow command without any pre-configured scope.","commands":{"allow":["set_shadow"],"deny":[]}},"allow-set-simple-fullscreen":{"identifier":"allow-set-simple-fullscreen","description":"Enables the set_simple_fullscreen command without any pre-configured scope.","commands":{"allow":["set_simple_fullscreen"],"deny":[]}},"allow-set-size":{"identifier":"allow-set-size","description":"Enables the set_size command without any pre-configured scope.","commands":{"allow":["set_size"],"deny":[]}},"allow-set-size-constraints":{"identifier":"allow-set-size-constraints","description":"Enables the set_size_constraints command without any pre-configured scope.","commands":{"allow":["set_size_constraints"],"deny":[]}},"allow-set-skip-taskbar":{"identifier":"allow-set-skip-taskbar","description":"Enables the set_skip_taskbar command without any pre-configured scope.","commands":{"allow":["set_skip_taskbar"],"deny":[]}},"allow-set-theme":{"identifier":"allow-set-theme","description":"Enables the set_theme command without any pre-configured scope.","commands":{"allow":["set_theme"],"deny":[]}},"allow-set-title":{"identifier":"allow-set-title","description":"Enables the set_title command without any pre-configured scope.","commands":{"allow":["set_title"],"deny":[]}},"allow-set-title-bar-style":{"identifier":"allow-set-title-bar-style","description":"Enables the set_title_bar_style command without any pre-configured scope.","commands":{"allow":["set_title_bar_style"],"deny":[]}},"allow-set-visible-on-all-workspaces":{"identifier":"allow-set-visible-on-all-workspaces","description":"Enables the set_visible_on_all_workspaces command without any pre-configured scope.","commands":{"allow":["set_visible_on_all_workspaces"],"deny":[]}},"allow-show":{"identifier":"allow-show","description":"Enables the show command without any pre-configured scope.","commands":{"allow":["show"],"deny":[]}},"allow-start-dragging":{"identifier":"allow-start-dragging","description":"Enables the start_dragging command without any pre-configured scope.","commands":{"allow":["start_dragging"],"deny":[]}},"allow-start-resize-dragging":{"identifier":"allow-start-resize-dragging","description":"Enables the start_resize_dragging command without any pre-configured scope.","commands":{"allow":["start_resize_dragging"],"deny":[]}},"allow-theme":{"identifier":"allow-theme","description":"Enables the theme command without any pre-configured scope.","commands":{"allow":["theme"],"deny":[]}},"allow-title":{"identifier":"allow-title","description":"Enables the title command without any pre-configured scope.","commands":{"allow":["title"],"deny":[]}},"allow-toggle-maximize":{"identifier":"allow-toggle-maximize","description":"Enables the toggle_maximize command without any pre-configured scope.","commands":{"allow":["toggle_maximize"],"deny":[]}},"allow-unmaximize":{"identifier":"allow-unmaximize","description":"Enables the unmaximize command without any pre-configured scope.","commands":{"allow":["unmaximize"],"deny":[]}},"allow-unminimize":{"identifier":"allow-unminimize","description":"Enables the unminimize command without any pre-configured scope.","commands":{"allow":["unminimize"],"deny":[]}},"deny-activity-name":{"identifier":"deny-activity-name","description":"Denies the activity_name command without any pre-configured scope.","commands":{"allow":[],"deny":["activity_name"]}},"deny-available-monitors":{"identifier":"deny-available-monitors","description":"Denies the available_monitors command without any pre-configured scope.","commands":{"allow":[],"deny":["available_monitors"]}},"deny-center":{"identifier":"deny-center","description":"Denies the center command without any pre-configured scope.","commands":{"allow":[],"deny":["center"]}},"deny-close":{"identifier":"deny-close","description":"Denies the close command without any pre-configured scope.","commands":{"allow":[],"deny":["close"]}},"deny-create":{"identifier":"deny-create","description":"Denies the create command without any pre-configured scope.","commands":{"allow":[],"deny":["create"]}},"deny-current-monitor":{"identifier":"deny-current-monitor","description":"Denies the current_monitor command without any pre-configured scope.","commands":{"allow":[],"deny":["current_monitor"]}},"deny-cursor-position":{"identifier":"deny-cursor-position","description":"Denies the cursor_position command without any pre-configured scope.","commands":{"allow":[],"deny":["cursor_position"]}},"deny-destroy":{"identifier":"deny-destroy","description":"Denies the destroy command without any pre-configured scope.","commands":{"allow":[],"deny":["destroy"]}},"deny-get-all-windows":{"identifier":"deny-get-all-windows","description":"Denies the get_all_windows command without any pre-configured scope.","commands":{"allow":[],"deny":["get_all_windows"]}},"deny-hide":{"identifier":"deny-hide","description":"Denies the hide command without any pre-configured scope.","commands":{"allow":[],"deny":["hide"]}},"deny-inner-position":{"identifier":"deny-inner-position","description":"Denies the inner_position command without any pre-configured scope.","commands":{"allow":[],"deny":["inner_position"]}},"deny-inner-size":{"identifier":"deny-inner-size","description":"Denies the inner_size command without any pre-configured scope.","commands":{"allow":[],"deny":["inner_size"]}},"deny-internal-toggle-maximize":{"identifier":"deny-internal-toggle-maximize","description":"Denies the internal_toggle_maximize command without any pre-configured scope.","commands":{"allow":[],"deny":["internal_toggle_maximize"]}},"deny-is-always-on-top":{"identifier":"deny-is-always-on-top","description":"Denies the is_always_on_top command without any pre-configured scope.","commands":{"allow":[],"deny":["is_always_on_top"]}},"deny-is-closable":{"identifier":"deny-is-closable","description":"Denies the is_closable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_closable"]}},"deny-is-decorated":{"identifier":"deny-is-decorated","description":"Denies the is_decorated command without any pre-configured scope.","commands":{"allow":[],"deny":["is_decorated"]}},"deny-is-enabled":{"identifier":"deny-is-enabled","description":"Denies the is_enabled command without any pre-configured scope.","commands":{"allow":[],"deny":["is_enabled"]}},"deny-is-focused":{"identifier":"deny-is-focused","description":"Denies the is_focused command without any pre-configured scope.","commands":{"allow":[],"deny":["is_focused"]}},"deny-is-fullscreen":{"identifier":"deny-is-fullscreen","description":"Denies the is_fullscreen command without any pre-configured scope.","commands":{"allow":[],"deny":["is_fullscreen"]}},"deny-is-maximizable":{"identifier":"deny-is-maximizable","description":"Denies the is_maximizable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_maximizable"]}},"deny-is-maximized":{"identifier":"deny-is-maximized","description":"Denies the is_maximized command without any pre-configured scope.","commands":{"allow":[],"deny":["is_maximized"]}},"deny-is-minimizable":{"identifier":"deny-is-minimizable","description":"Denies the is_minimizable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_minimizable"]}},"deny-is-minimized":{"identifier":"deny-is-minimized","description":"Denies the is_minimized command without any pre-configured scope.","commands":{"allow":[],"deny":["is_minimized"]}},"deny-is-resizable":{"identifier":"deny-is-resizable","description":"Denies the is_resizable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_resizable"]}},"deny-is-visible":{"identifier":"deny-is-visible","description":"Denies the is_visible command without any pre-configured scope.","commands":{"allow":[],"deny":["is_visible"]}},"deny-maximize":{"identifier":"deny-maximize","description":"Denies the maximize command without any pre-configured scope.","commands":{"allow":[],"deny":["maximize"]}},"deny-minimize":{"identifier":"deny-minimize","description":"Denies the minimize command without any pre-configured scope.","commands":{"allow":[],"deny":["minimize"]}},"deny-monitor-from-point":{"identifier":"deny-monitor-from-point","description":"Denies the monitor_from_point command without any pre-configured scope.","commands":{"allow":[],"deny":["monitor_from_point"]}},"deny-outer-position":{"identifier":"deny-outer-position","description":"Denies the outer_position command without any pre-configured scope.","commands":{"allow":[],"deny":["outer_position"]}},"deny-outer-size":{"identifier":"deny-outer-size","description":"Denies the outer_size command without any pre-configured scope.","commands":{"allow":[],"deny":["outer_size"]}},"deny-primary-monitor":{"identifier":"deny-primary-monitor","description":"Denies the primary_monitor command without any pre-configured scope.","commands":{"allow":[],"deny":["primary_monitor"]}},"deny-request-user-attention":{"identifier":"deny-request-user-attention","description":"Denies the request_user_attention command without any pre-configured scope.","commands":{"allow":[],"deny":["request_user_attention"]}},"deny-scale-factor":{"identifier":"deny-scale-factor","description":"Denies the scale_factor command without any pre-configured scope.","commands":{"allow":[],"deny":["scale_factor"]}},"deny-scene-identifier":{"identifier":"deny-scene-identifier","description":"Denies the scene_identifier command without any pre-configured scope.","commands":{"allow":[],"deny":["scene_identifier"]}},"deny-set-always-on-bottom":{"identifier":"deny-set-always-on-bottom","description":"Denies the set_always_on_bottom command without any pre-configured scope.","commands":{"allow":[],"deny":["set_always_on_bottom"]}},"deny-set-always-on-top":{"identifier":"deny-set-always-on-top","description":"Denies the set_always_on_top command without any pre-configured scope.","commands":{"allow":[],"deny":["set_always_on_top"]}},"deny-set-background-color":{"identifier":"deny-set-background-color","description":"Denies the set_background_color command without any pre-configured scope.","commands":{"allow":[],"deny":["set_background_color"]}},"deny-set-badge-count":{"identifier":"deny-set-badge-count","description":"Denies the set_badge_count command without any pre-configured scope.","commands":{"allow":[],"deny":["set_badge_count"]}},"deny-set-badge-label":{"identifier":"deny-set-badge-label","description":"Denies the set_badge_label command without any pre-configured scope.","commands":{"allow":[],"deny":["set_badge_label"]}},"deny-set-closable":{"identifier":"deny-set-closable","description":"Denies the set_closable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_closable"]}},"deny-set-content-protected":{"identifier":"deny-set-content-protected","description":"Denies the set_content_protected command without any pre-configured scope.","commands":{"allow":[],"deny":["set_content_protected"]}},"deny-set-cursor-grab":{"identifier":"deny-set-cursor-grab","description":"Denies the set_cursor_grab command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_grab"]}},"deny-set-cursor-icon":{"identifier":"deny-set-cursor-icon","description":"Denies the set_cursor_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_icon"]}},"deny-set-cursor-position":{"identifier":"deny-set-cursor-position","description":"Denies the set_cursor_position command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_position"]}},"deny-set-cursor-visible":{"identifier":"deny-set-cursor-visible","description":"Denies the set_cursor_visible command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_visible"]}},"deny-set-decorations":{"identifier":"deny-set-decorations","description":"Denies the set_decorations command without any pre-configured scope.","commands":{"allow":[],"deny":["set_decorations"]}},"deny-set-effects":{"identifier":"deny-set-effects","description":"Denies the set_effects command without any pre-configured scope.","commands":{"allow":[],"deny":["set_effects"]}},"deny-set-enabled":{"identifier":"deny-set-enabled","description":"Denies the set_enabled command without any pre-configured scope.","commands":{"allow":[],"deny":["set_enabled"]}},"deny-set-focus":{"identifier":"deny-set-focus","description":"Denies the set_focus command without any pre-configured scope.","commands":{"allow":[],"deny":["set_focus"]}},"deny-set-focusable":{"identifier":"deny-set-focusable","description":"Denies the set_focusable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_focusable"]}},"deny-set-fullscreen":{"identifier":"deny-set-fullscreen","description":"Denies the set_fullscreen command without any pre-configured scope.","commands":{"allow":[],"deny":["set_fullscreen"]}},"deny-set-icon":{"identifier":"deny-set-icon","description":"Denies the set_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon"]}},"deny-set-ignore-cursor-events":{"identifier":"deny-set-ignore-cursor-events","description":"Denies the set_ignore_cursor_events command without any pre-configured scope.","commands":{"allow":[],"deny":["set_ignore_cursor_events"]}},"deny-set-max-size":{"identifier":"deny-set-max-size","description":"Denies the set_max_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_max_size"]}},"deny-set-maximizable":{"identifier":"deny-set-maximizable","description":"Denies the set_maximizable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_maximizable"]}},"deny-set-min-size":{"identifier":"deny-set-min-size","description":"Denies the set_min_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_min_size"]}},"deny-set-minimizable":{"identifier":"deny-set-minimizable","description":"Denies the set_minimizable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_minimizable"]}},"deny-set-overlay-icon":{"identifier":"deny-set-overlay-icon","description":"Denies the set_overlay_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_overlay_icon"]}},"deny-set-position":{"identifier":"deny-set-position","description":"Denies the set_position command without any pre-configured scope.","commands":{"allow":[],"deny":["set_position"]}},"deny-set-progress-bar":{"identifier":"deny-set-progress-bar","description":"Denies the set_progress_bar command without any pre-configured scope.","commands":{"allow":[],"deny":["set_progress_bar"]}},"deny-set-resizable":{"identifier":"deny-set-resizable","description":"Denies the set_resizable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_resizable"]}},"deny-set-shadow":{"identifier":"deny-set-shadow","description":"Denies the set_shadow command without any pre-configured scope.","commands":{"allow":[],"deny":["set_shadow"]}},"deny-set-simple-fullscreen":{"identifier":"deny-set-simple-fullscreen","description":"Denies the set_simple_fullscreen command without any pre-configured scope.","commands":{"allow":[],"deny":["set_simple_fullscreen"]}},"deny-set-size":{"identifier":"deny-set-size","description":"Denies the set_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_size"]}},"deny-set-size-constraints":{"identifier":"deny-set-size-constraints","description":"Denies the set_size_constraints command without any pre-configured scope.","commands":{"allow":[],"deny":["set_size_constraints"]}},"deny-set-skip-taskbar":{"identifier":"deny-set-skip-taskbar","description":"Denies the set_skip_taskbar command without any pre-configured scope.","commands":{"allow":[],"deny":["set_skip_taskbar"]}},"deny-set-theme":{"identifier":"deny-set-theme","description":"Denies the set_theme command without any pre-configured scope.","commands":{"allow":[],"deny":["set_theme"]}},"deny-set-title":{"identifier":"deny-set-title","description":"Denies the set_title command without any pre-configured scope.","commands":{"allow":[],"deny":["set_title"]}},"deny-set-title-bar-style":{"identifier":"deny-set-title-bar-style","description":"Denies the set_title_bar_style command without any pre-configured scope.","commands":{"allow":[],"deny":["set_title_bar_style"]}},"deny-set-visible-on-all-workspaces":{"identifier":"deny-set-visible-on-all-workspaces","description":"Denies the set_visible_on_all_workspaces command without any pre-configured scope.","commands":{"allow":[],"deny":["set_visible_on_all_workspaces"]}},"deny-show":{"identifier":"deny-show","description":"Denies the show command without any pre-configured scope.","commands":{"allow":[],"deny":["show"]}},"deny-start-dragging":{"identifier":"deny-start-dragging","description":"Denies the start_dragging command without any pre-configured scope.","commands":{"allow":[],"deny":["start_dragging"]}},"deny-start-resize-dragging":{"identifier":"deny-start-resize-dragging","description":"Denies the start_resize_dragging command without any pre-configured scope.","commands":{"allow":[],"deny":["start_resize_dragging"]}},"deny-theme":{"identifier":"deny-theme","description":"Denies the theme command without any pre-configured scope.","commands":{"allow":[],"deny":["theme"]}},"deny-title":{"identifier":"deny-title","description":"Denies the title command without any pre-configured scope.","commands":{"allow":[],"deny":["title"]}},"deny-toggle-maximize":{"identifier":"deny-toggle-maximize","description":"Denies the toggle_maximize command without any pre-configured scope.","commands":{"allow":[],"deny":["toggle_maximize"]}},"deny-unmaximize":{"identifier":"deny-unmaximize","description":"Denies the unmaximize command without any pre-configured scope.","commands":{"allow":[],"deny":["unmaximize"]}},"deny-unminimize":{"identifier":"deny-unminimize","description":"Denies the unminimize command without any pre-configured scope.","commands":{"allow":[],"deny":["unminimize"]}}},"permission_sets":{},"global_scope_schema":null},"shell":{"default_permission":{"identifier":"default","description":"This permission set configures which\nshell functionality is exposed by default.\n\n#### Granted Permissions\n\nIt allows to use the `open` functionality with a reasonable\nscope pre-configured. It will allow opening `http(s)://`,\n`tel:` and `mailto:` links.\n","permissions":["allow-open"]},"permissions":{"allow-execute":{"identifier":"allow-execute","description":"Enables the execute command without any pre-configured scope.","commands":{"allow":["execute"],"deny":[]}},"allow-kill":{"identifier":"allow-kill","description":"Enables the kill command without any pre-configured scope.","commands":{"allow":["kill"],"deny":[]}},"allow-open":{"identifier":"allow-open","description":"Enables the open command without any pre-configured scope.","commands":{"allow":["open"],"deny":[]}},"allow-spawn":{"identifier":"allow-spawn","description":"Enables the spawn command without any pre-configured scope.","commands":{"allow":["spawn"],"deny":[]}},"allow-stdin-write":{"identifier":"allow-stdin-write","description":"Enables the stdin_write command without any pre-configured scope.","commands":{"allow":["stdin_write"],"deny":[]}},"deny-execute":{"identifier":"deny-execute","description":"Denies the execute command without any pre-configured scope.","commands":{"allow":[],"deny":["execute"]}},"deny-kill":{"identifier":"deny-kill","description":"Denies the kill command without any pre-configured scope.","commands":{"allow":[],"deny":["kill"]}},"deny-open":{"identifier":"deny-open","description":"Denies the open command without any pre-configured scope.","commands":{"allow":[],"deny":["open"]}},"deny-spawn":{"identifier":"deny-spawn","description":"Denies the spawn command without any pre-configured scope.","commands":{"allow":[],"deny":["spawn"]}},"deny-stdin-write":{"identifier":"deny-stdin-write","description":"Denies the stdin_write command without any pre-configured scope.","commands":{"allow":[],"deny":["stdin_write"]}}},"permission_sets":{},"global_scope_schema":{"$schema":"http://json-schema.org/draft-07/schema#","anyOf":[{"additionalProperties":false,"properties":{"args":{"allOf":[{"$ref":"#/definitions/ShellScopeEntryAllowedArgs"}],"description":"The allowed arguments for the command execution."},"cmd":{"description":"The command name. It can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.","type":"string"},"name":{"description":"The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.","type":"string"}},"required":["cmd","name"],"type":"object"},{"additionalProperties":false,"properties":{"args":{"allOf":[{"$ref":"#/definitions/ShellScopeEntryAllowedArgs"}],"description":"The allowed arguments for the command execution."},"name":{"description":"The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.","type":"string"},"sidecar":{"description":"If this command is a sidecar command.","type":"boolean"}},"required":["name","sidecar"],"type":"object"}],"definitions":{"ShellScopeEntryAllowedArg":{"anyOf":[{"description":"A non-configurable argument that is passed to the command in the order it was specified.","type":"string"},{"additionalProperties":false,"description":"A variable that is set while calling the command from the webview API.","properties":{"raw":{"default":false,"description":"Marks the validator as a raw regex, meaning the plugin should not make any modification at runtime.\n\nThis means the regex will not match on the entire string by default, which might be exploited if your regex allow unexpected input to be considered valid. When using this option, make sure your regex is correct.","type":"boolean"},"validator":{"description":"[regex] validator to require passed values to conform to an expected input.\n\nThis will require the argument value passed to this variable to match the `validator` regex before it will be executed.\n\nThe regex string is by default surrounded by `^...$` to match the full string. For example the `https?://\\w+` regex would be registered as `^https?://\\w+$`.\n\n[regex]: ","type":"string"}},"required":["validator"],"type":"object"}],"description":"A command argument allowed to be executed by the webview API."},"ShellScopeEntryAllowedArgs":{"anyOf":[{"description":"Use a simple boolean to allow all or disable all arguments to this command configuration.","type":"boolean"},{"description":"A specific set of [`ShellScopeEntryAllowedArg`] that are valid to call for the command configuration.","items":{"$ref":"#/definitions/ShellScopeEntryAllowedArg"},"type":"array"}],"description":"A set of command arguments allowed to be executed by the webview API.\n\nA value of `true` will allow any arguments to be passed to the command. `false` will disable all arguments. A list of [`ShellScopeEntryAllowedArg`] will set those arguments as the only valid arguments to be passed to the attached command configuration."}},"description":"Shell scope entry.","title":"ShellScopeEntry"}}}
\ No newline at end of file
diff --git a/src-tauri/gen/schemas/desktop-schema.json b/src-tauri/gen/schemas/desktop-schema.json
index f827fe175..d1e536142 100644
--- a/src-tauri/gen/schemas/desktop-schema.json
+++ b/src-tauri/gen/schemas/desktop-schema.json
@@ -393,10 +393,10 @@
"markdownDescription": "Default core plugins set.\n#### This default permission set includes:\n\n- `core:path:default`\n- `core:event:default`\n- `core:window:default`\n- `core:webview:default`\n- `core:app:default`\n- `core:image:default`\n- `core:resources:default`\n- `core:menu:default`\n- `core:tray:default`"
},
{
- "description": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-version`\n- `allow-name`\n- `allow-tauri-version`\n- `allow-identifier`\n- `allow-bundle-type`\n- `allow-register-listener`\n- `allow-remove-listener`",
+ "description": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-version`\n- `allow-name`\n- `allow-tauri-version`\n- `allow-identifier`\n- `allow-bundle-type`\n- `allow-register-listener`\n- `allow-remove-listener`\n- `allow-supports-multiple-windows`",
"type": "string",
"const": "core:app:default",
- "markdownDescription": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-version`\n- `allow-name`\n- `allow-tauri-version`\n- `allow-identifier`\n- `allow-bundle-type`\n- `allow-register-listener`\n- `allow-remove-listener`"
+ "markdownDescription": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-version`\n- `allow-name`\n- `allow-tauri-version`\n- `allow-identifier`\n- `allow-bundle-type`\n- `allow-register-listener`\n- `allow-remove-listener`\n- `allow-supports-multiple-windows`"
},
{
"description": "Enables the app_hide command without any pre-configured scope.",
@@ -470,6 +470,12 @@
"const": "core:app:allow-set-dock-visibility",
"markdownDescription": "Enables the set_dock_visibility command without any pre-configured scope."
},
+ {
+ "description": "Enables the supports_multiple_windows command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:app:allow-supports-multiple-windows",
+ "markdownDescription": "Enables the supports_multiple_windows command without any pre-configured scope."
+ },
{
"description": "Enables the tauri_version command without any pre-configured scope.",
"type": "string",
@@ -554,6 +560,12 @@
"const": "core:app:deny-set-dock-visibility",
"markdownDescription": "Denies the set_dock_visibility command without any pre-configured scope."
},
+ {
+ "description": "Denies the supports_multiple_windows command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:app:deny-supports-multiple-windows",
+ "markdownDescription": "Denies the supports_multiple_windows command without any pre-configured scope."
+ },
{
"description": "Denies the tauri_version command without any pre-configured scope.",
"type": "string",
@@ -1077,10 +1089,10 @@
"markdownDescription": "Denies the close command without any pre-configured scope."
},
{
- "description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-get-by-id`\n- `allow-remove-by-id`\n- `allow-set-icon`\n- `allow-set-menu`\n- `allow-set-tooltip`\n- `allow-set-title`\n- `allow-set-visible`\n- `allow-set-temp-dir-path`\n- `allow-set-icon-as-template`\n- `allow-set-show-menu-on-left-click`",
+ "description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-get-by-id`\n- `allow-remove-by-id`\n- `allow-set-icon`\n- `allow-set-menu`\n- `allow-set-tooltip`\n- `allow-set-title`\n- `allow-set-visible`\n- `allow-set-temp-dir-path`\n- `allow-set-icon-as-template`\n- `allow-set-icon-with-as-template`\n- `allow-set-show-menu-on-left-click`",
"type": "string",
"const": "core:tray:default",
- "markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-get-by-id`\n- `allow-remove-by-id`\n- `allow-set-icon`\n- `allow-set-menu`\n- `allow-set-tooltip`\n- `allow-set-title`\n- `allow-set-visible`\n- `allow-set-temp-dir-path`\n- `allow-set-icon-as-template`\n- `allow-set-show-menu-on-left-click`"
+ "markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-get-by-id`\n- `allow-remove-by-id`\n- `allow-set-icon`\n- `allow-set-menu`\n- `allow-set-tooltip`\n- `allow-set-title`\n- `allow-set-visible`\n- `allow-set-temp-dir-path`\n- `allow-set-icon-as-template`\n- `allow-set-icon-with-as-template`\n- `allow-set-show-menu-on-left-click`"
},
{
"description": "Enables the get_by_id command without any pre-configured scope.",
@@ -1112,6 +1124,12 @@
"const": "core:tray:allow-set-icon-as-template",
"markdownDescription": "Enables the set_icon_as_template command without any pre-configured scope."
},
+ {
+ "description": "Enables the set_icon_with_as_template command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:tray:allow-set-icon-with-as-template",
+ "markdownDescription": "Enables the set_icon_with_as_template command without any pre-configured scope."
+ },
{
"description": "Enables the set_menu command without any pre-configured scope.",
"type": "string",
@@ -1178,6 +1196,12 @@
"const": "core:tray:deny-set-icon-as-template",
"markdownDescription": "Denies the set_icon_as_template command without any pre-configured scope."
},
+ {
+ "description": "Denies the set_icon_with_as_template command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:tray:deny-set-icon-with-as-template",
+ "markdownDescription": "Denies the set_icon_with_as_template command without any pre-configured scope."
+ },
{
"description": "Denies the set_menu command without any pre-configured scope.",
"type": "string",
@@ -1437,10 +1461,16 @@
"markdownDescription": "Denies the webview_size command without any pre-configured scope."
},
{
- "description": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-get-all-windows`\n- `allow-scale-factor`\n- `allow-inner-position`\n- `allow-outer-position`\n- `allow-inner-size`\n- `allow-outer-size`\n- `allow-is-fullscreen`\n- `allow-is-minimized`\n- `allow-is-maximized`\n- `allow-is-focused`\n- `allow-is-decorated`\n- `allow-is-resizable`\n- `allow-is-maximizable`\n- `allow-is-minimizable`\n- `allow-is-closable`\n- `allow-is-visible`\n- `allow-is-enabled`\n- `allow-title`\n- `allow-current-monitor`\n- `allow-primary-monitor`\n- `allow-monitor-from-point`\n- `allow-available-monitors`\n- `allow-cursor-position`\n- `allow-theme`\n- `allow-is-always-on-top`\n- `allow-internal-toggle-maximize`",
+ "description": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-get-all-windows`\n- `allow-scale-factor`\n- `allow-inner-position`\n- `allow-outer-position`\n- `allow-inner-size`\n- `allow-outer-size`\n- `allow-is-fullscreen`\n- `allow-is-minimized`\n- `allow-is-maximized`\n- `allow-is-focused`\n- `allow-is-decorated`\n- `allow-is-resizable`\n- `allow-is-maximizable`\n- `allow-is-minimizable`\n- `allow-is-closable`\n- `allow-is-visible`\n- `allow-is-enabled`\n- `allow-title`\n- `allow-current-monitor`\n- `allow-primary-monitor`\n- `allow-monitor-from-point`\n- `allow-available-monitors`\n- `allow-cursor-position`\n- `allow-theme`\n- `allow-is-always-on-top`\n- `allow-activity-name`\n- `allow-scene-identifier`\n- `allow-internal-toggle-maximize`",
"type": "string",
"const": "core:window:default",
- "markdownDescription": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-get-all-windows`\n- `allow-scale-factor`\n- `allow-inner-position`\n- `allow-outer-position`\n- `allow-inner-size`\n- `allow-outer-size`\n- `allow-is-fullscreen`\n- `allow-is-minimized`\n- `allow-is-maximized`\n- `allow-is-focused`\n- `allow-is-decorated`\n- `allow-is-resizable`\n- `allow-is-maximizable`\n- `allow-is-minimizable`\n- `allow-is-closable`\n- `allow-is-visible`\n- `allow-is-enabled`\n- `allow-title`\n- `allow-current-monitor`\n- `allow-primary-monitor`\n- `allow-monitor-from-point`\n- `allow-available-monitors`\n- `allow-cursor-position`\n- `allow-theme`\n- `allow-is-always-on-top`\n- `allow-internal-toggle-maximize`"
+ "markdownDescription": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-get-all-windows`\n- `allow-scale-factor`\n- `allow-inner-position`\n- `allow-outer-position`\n- `allow-inner-size`\n- `allow-outer-size`\n- `allow-is-fullscreen`\n- `allow-is-minimized`\n- `allow-is-maximized`\n- `allow-is-focused`\n- `allow-is-decorated`\n- `allow-is-resizable`\n- `allow-is-maximizable`\n- `allow-is-minimizable`\n- `allow-is-closable`\n- `allow-is-visible`\n- `allow-is-enabled`\n- `allow-title`\n- `allow-current-monitor`\n- `allow-primary-monitor`\n- `allow-monitor-from-point`\n- `allow-available-monitors`\n- `allow-cursor-position`\n- `allow-theme`\n- `allow-is-always-on-top`\n- `allow-activity-name`\n- `allow-scene-identifier`\n- `allow-internal-toggle-maximize`"
+ },
+ {
+ "description": "Enables the activity_name command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-activity-name",
+ "markdownDescription": "Enables the activity_name command without any pre-configured scope."
},
{
"description": "Enables the available_monitors command without any pre-configured scope.",
@@ -1634,6 +1664,12 @@
"const": "core:window:allow-scale-factor",
"markdownDescription": "Enables the scale_factor command without any pre-configured scope."
},
+ {
+ "description": "Enables the scene_identifier command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-scene-identifier",
+ "markdownDescription": "Enables the scene_identifier command without any pre-configured scope."
+ },
{
"description": "Enables the set_always_on_bottom command without any pre-configured scope.",
"type": "string",
@@ -1898,6 +1934,12 @@
"const": "core:window:allow-unminimize",
"markdownDescription": "Enables the unminimize command without any pre-configured scope."
},
+ {
+ "description": "Denies the activity_name command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-activity-name",
+ "markdownDescription": "Denies the activity_name command without any pre-configured scope."
+ },
{
"description": "Denies the available_monitors command without any pre-configured scope.",
"type": "string",
@@ -2090,6 +2132,12 @@
"const": "core:window:deny-scale-factor",
"markdownDescription": "Denies the scale_factor command without any pre-configured scope."
},
+ {
+ "description": "Denies the scene_identifier command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-scene-identifier",
+ "markdownDescription": "Denies the scene_identifier command without any pre-configured scope."
+ },
{
"description": "Denies the set_always_on_bottom command without any pre-configured scope.",
"type": "string",
diff --git a/src-tauri/gen/schemas/macOS-schema.json b/src-tauri/gen/schemas/macOS-schema.json
index f827fe175..d1e536142 100644
--- a/src-tauri/gen/schemas/macOS-schema.json
+++ b/src-tauri/gen/schemas/macOS-schema.json
@@ -393,10 +393,10 @@
"markdownDescription": "Default core plugins set.\n#### This default permission set includes:\n\n- `core:path:default`\n- `core:event:default`\n- `core:window:default`\n- `core:webview:default`\n- `core:app:default`\n- `core:image:default`\n- `core:resources:default`\n- `core:menu:default`\n- `core:tray:default`"
},
{
- "description": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-version`\n- `allow-name`\n- `allow-tauri-version`\n- `allow-identifier`\n- `allow-bundle-type`\n- `allow-register-listener`\n- `allow-remove-listener`",
+ "description": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-version`\n- `allow-name`\n- `allow-tauri-version`\n- `allow-identifier`\n- `allow-bundle-type`\n- `allow-register-listener`\n- `allow-remove-listener`\n- `allow-supports-multiple-windows`",
"type": "string",
"const": "core:app:default",
- "markdownDescription": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-version`\n- `allow-name`\n- `allow-tauri-version`\n- `allow-identifier`\n- `allow-bundle-type`\n- `allow-register-listener`\n- `allow-remove-listener`"
+ "markdownDescription": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-version`\n- `allow-name`\n- `allow-tauri-version`\n- `allow-identifier`\n- `allow-bundle-type`\n- `allow-register-listener`\n- `allow-remove-listener`\n- `allow-supports-multiple-windows`"
},
{
"description": "Enables the app_hide command without any pre-configured scope.",
@@ -470,6 +470,12 @@
"const": "core:app:allow-set-dock-visibility",
"markdownDescription": "Enables the set_dock_visibility command without any pre-configured scope."
},
+ {
+ "description": "Enables the supports_multiple_windows command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:app:allow-supports-multiple-windows",
+ "markdownDescription": "Enables the supports_multiple_windows command without any pre-configured scope."
+ },
{
"description": "Enables the tauri_version command without any pre-configured scope.",
"type": "string",
@@ -554,6 +560,12 @@
"const": "core:app:deny-set-dock-visibility",
"markdownDescription": "Denies the set_dock_visibility command without any pre-configured scope."
},
+ {
+ "description": "Denies the supports_multiple_windows command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:app:deny-supports-multiple-windows",
+ "markdownDescription": "Denies the supports_multiple_windows command without any pre-configured scope."
+ },
{
"description": "Denies the tauri_version command without any pre-configured scope.",
"type": "string",
@@ -1077,10 +1089,10 @@
"markdownDescription": "Denies the close command without any pre-configured scope."
},
{
- "description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-get-by-id`\n- `allow-remove-by-id`\n- `allow-set-icon`\n- `allow-set-menu`\n- `allow-set-tooltip`\n- `allow-set-title`\n- `allow-set-visible`\n- `allow-set-temp-dir-path`\n- `allow-set-icon-as-template`\n- `allow-set-show-menu-on-left-click`",
+ "description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-get-by-id`\n- `allow-remove-by-id`\n- `allow-set-icon`\n- `allow-set-menu`\n- `allow-set-tooltip`\n- `allow-set-title`\n- `allow-set-visible`\n- `allow-set-temp-dir-path`\n- `allow-set-icon-as-template`\n- `allow-set-icon-with-as-template`\n- `allow-set-show-menu-on-left-click`",
"type": "string",
"const": "core:tray:default",
- "markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-get-by-id`\n- `allow-remove-by-id`\n- `allow-set-icon`\n- `allow-set-menu`\n- `allow-set-tooltip`\n- `allow-set-title`\n- `allow-set-visible`\n- `allow-set-temp-dir-path`\n- `allow-set-icon-as-template`\n- `allow-set-show-menu-on-left-click`"
+ "markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-get-by-id`\n- `allow-remove-by-id`\n- `allow-set-icon`\n- `allow-set-menu`\n- `allow-set-tooltip`\n- `allow-set-title`\n- `allow-set-visible`\n- `allow-set-temp-dir-path`\n- `allow-set-icon-as-template`\n- `allow-set-icon-with-as-template`\n- `allow-set-show-menu-on-left-click`"
},
{
"description": "Enables the get_by_id command without any pre-configured scope.",
@@ -1112,6 +1124,12 @@
"const": "core:tray:allow-set-icon-as-template",
"markdownDescription": "Enables the set_icon_as_template command without any pre-configured scope."
},
+ {
+ "description": "Enables the set_icon_with_as_template command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:tray:allow-set-icon-with-as-template",
+ "markdownDescription": "Enables the set_icon_with_as_template command without any pre-configured scope."
+ },
{
"description": "Enables the set_menu command without any pre-configured scope.",
"type": "string",
@@ -1178,6 +1196,12 @@
"const": "core:tray:deny-set-icon-as-template",
"markdownDescription": "Denies the set_icon_as_template command without any pre-configured scope."
},
+ {
+ "description": "Denies the set_icon_with_as_template command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:tray:deny-set-icon-with-as-template",
+ "markdownDescription": "Denies the set_icon_with_as_template command without any pre-configured scope."
+ },
{
"description": "Denies the set_menu command without any pre-configured scope.",
"type": "string",
@@ -1437,10 +1461,16 @@
"markdownDescription": "Denies the webview_size command without any pre-configured scope."
},
{
- "description": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-get-all-windows`\n- `allow-scale-factor`\n- `allow-inner-position`\n- `allow-outer-position`\n- `allow-inner-size`\n- `allow-outer-size`\n- `allow-is-fullscreen`\n- `allow-is-minimized`\n- `allow-is-maximized`\n- `allow-is-focused`\n- `allow-is-decorated`\n- `allow-is-resizable`\n- `allow-is-maximizable`\n- `allow-is-minimizable`\n- `allow-is-closable`\n- `allow-is-visible`\n- `allow-is-enabled`\n- `allow-title`\n- `allow-current-monitor`\n- `allow-primary-monitor`\n- `allow-monitor-from-point`\n- `allow-available-monitors`\n- `allow-cursor-position`\n- `allow-theme`\n- `allow-is-always-on-top`\n- `allow-internal-toggle-maximize`",
+ "description": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-get-all-windows`\n- `allow-scale-factor`\n- `allow-inner-position`\n- `allow-outer-position`\n- `allow-inner-size`\n- `allow-outer-size`\n- `allow-is-fullscreen`\n- `allow-is-minimized`\n- `allow-is-maximized`\n- `allow-is-focused`\n- `allow-is-decorated`\n- `allow-is-resizable`\n- `allow-is-maximizable`\n- `allow-is-minimizable`\n- `allow-is-closable`\n- `allow-is-visible`\n- `allow-is-enabled`\n- `allow-title`\n- `allow-current-monitor`\n- `allow-primary-monitor`\n- `allow-monitor-from-point`\n- `allow-available-monitors`\n- `allow-cursor-position`\n- `allow-theme`\n- `allow-is-always-on-top`\n- `allow-activity-name`\n- `allow-scene-identifier`\n- `allow-internal-toggle-maximize`",
"type": "string",
"const": "core:window:default",
- "markdownDescription": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-get-all-windows`\n- `allow-scale-factor`\n- `allow-inner-position`\n- `allow-outer-position`\n- `allow-inner-size`\n- `allow-outer-size`\n- `allow-is-fullscreen`\n- `allow-is-minimized`\n- `allow-is-maximized`\n- `allow-is-focused`\n- `allow-is-decorated`\n- `allow-is-resizable`\n- `allow-is-maximizable`\n- `allow-is-minimizable`\n- `allow-is-closable`\n- `allow-is-visible`\n- `allow-is-enabled`\n- `allow-title`\n- `allow-current-monitor`\n- `allow-primary-monitor`\n- `allow-monitor-from-point`\n- `allow-available-monitors`\n- `allow-cursor-position`\n- `allow-theme`\n- `allow-is-always-on-top`\n- `allow-internal-toggle-maximize`"
+ "markdownDescription": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-get-all-windows`\n- `allow-scale-factor`\n- `allow-inner-position`\n- `allow-outer-position`\n- `allow-inner-size`\n- `allow-outer-size`\n- `allow-is-fullscreen`\n- `allow-is-minimized`\n- `allow-is-maximized`\n- `allow-is-focused`\n- `allow-is-decorated`\n- `allow-is-resizable`\n- `allow-is-maximizable`\n- `allow-is-minimizable`\n- `allow-is-closable`\n- `allow-is-visible`\n- `allow-is-enabled`\n- `allow-title`\n- `allow-current-monitor`\n- `allow-primary-monitor`\n- `allow-monitor-from-point`\n- `allow-available-monitors`\n- `allow-cursor-position`\n- `allow-theme`\n- `allow-is-always-on-top`\n- `allow-activity-name`\n- `allow-scene-identifier`\n- `allow-internal-toggle-maximize`"
+ },
+ {
+ "description": "Enables the activity_name command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-activity-name",
+ "markdownDescription": "Enables the activity_name command without any pre-configured scope."
},
{
"description": "Enables the available_monitors command without any pre-configured scope.",
@@ -1634,6 +1664,12 @@
"const": "core:window:allow-scale-factor",
"markdownDescription": "Enables the scale_factor command without any pre-configured scope."
},
+ {
+ "description": "Enables the scene_identifier command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-scene-identifier",
+ "markdownDescription": "Enables the scene_identifier command without any pre-configured scope."
+ },
{
"description": "Enables the set_always_on_bottom command without any pre-configured scope.",
"type": "string",
@@ -1898,6 +1934,12 @@
"const": "core:window:allow-unminimize",
"markdownDescription": "Enables the unminimize command without any pre-configured scope."
},
+ {
+ "description": "Denies the activity_name command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-activity-name",
+ "markdownDescription": "Denies the activity_name command without any pre-configured scope."
+ },
{
"description": "Denies the available_monitors command without any pre-configured scope.",
"type": "string",
@@ -2090,6 +2132,12 @@
"const": "core:window:deny-scale-factor",
"markdownDescription": "Denies the scale_factor command without any pre-configured scope."
},
+ {
+ "description": "Denies the scene_identifier command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-scene-identifier",
+ "markdownDescription": "Denies the scene_identifier command without any pre-configured scope."
+ },
{
"description": "Denies the set_always_on_bottom command without any pre-configured scope.",
"type": "string",
diff --git a/src/app/defaults.test.tsx b/src/app/defaults.test.tsx
new file mode 100644
index 000000000..0e5333787
--- /dev/null
+++ b/src/app/defaults.test.tsx
@@ -0,0 +1,52 @@
+/**
+ * app/defaults — pure helpers and the i18n-backed badge/caption surfaces.
+ * Covers formatMnemonic (newline → space + trim), the deprecated
+ * getAccountBadgeTitle constant, the useAccountBadgeTitle hook, and the
+ * MnemonicErrorCaption list.
+ */
+
+import React from 'react';
+
+import { render, renderHook } from '@testing-library/react';
+
+import { MnemonicErrorCaption, formatMnemonic, getAccountBadgeTitle, useAccountBadgeTitle } from './defaults';
+
+jest.mock('react-i18next', () => ({
+ useTranslation: () => ({
+ t: (key: string) => key
+ })
+}));
+
+describe('app/defaults', () => {
+ describe('formatMnemonic', () => {
+ it('replaces newlines with spaces and trims surrounding whitespace', () => {
+ expect(formatMnemonic(' alpha\nbravo\ncharlie ')).toBe('alpha bravo charlie');
+ });
+
+ it('leaves an already-clean single-line phrase untouched', () => {
+ expect(formatMnemonic('alpha bravo charlie')).toBe('alpha bravo charlie');
+ });
+ });
+
+ describe('getAccountBadgeTitle', () => {
+ it('returns the deprecated static "Imported" label', () => {
+ expect(getAccountBadgeTitle()).toBe('Imported');
+ });
+ });
+
+ describe('useAccountBadgeTitle', () => {
+ it('returns the translated imported-account label', () => {
+ const { result } = renderHook(() => useAccountBadgeTitle());
+ expect(result.current).toBe('importedAccount');
+ });
+ });
+
+ describe('MnemonicErrorCaption', () => {
+ it('renders the three mnemonic-constraint list items', () => {
+ const { getByText } = render();
+ expect(getByText('mnemonicWordsAmountConstraint')).toBeInTheDocument();
+ expect(getByText('mnemonicSpacingConstraint')).toBeInTheDocument();
+ expect(getByText('justValidPreGeneratedMnemonic')).toBeInTheDocument();
+ });
+ });
+});
diff --git a/src/app/pages/Explore.tsx b/src/app/pages/Explore.tsx
index 4cb6ee970..052afa4e8 100644
--- a/src/app/pages/Explore.tsx
+++ b/src/app/pages/Explore.tsx
@@ -4,6 +4,7 @@ import classNames from 'clsx';
import useMidenFaucetId from 'app/hooks/useMidenFaucetId';
import Header from 'app/layouts/PageLayout/Header';
+import { ActivateHotKeyBanner } from 'app/templates/ActivateHotKeyBanner';
import { ConnectivityIssueBanner } from 'components/ConnectivityIssueBanner';
import { ActionButtons } from 'components/explore/ActionButtons';
import { PriceChangeBadge } from 'components/explore/PriceChangeBadge';
@@ -64,7 +65,9 @@ const Explore: FC = () => {
}
const promises = notesToClaim.map(async note => {
- await initiateConsumeTransaction(account.publicKey, note, isDelegatedProvingEnabled);
+ // `background: true` — this is a silent auto-consume, so on Guardian
+ // accounts it's cold-signed (no biometric prompt). See initiateConsumeTransaction.
+ await initiateConsumeTransaction(account.publicKey, note, isDelegatedProvingEnabled, true);
});
await Promise.all(promises);
mutateClaimableNotes();
@@ -138,6 +141,7 @@ const Explore: FC = () => {
+
diff --git a/src/app/pages/Explore/Tokens.test.tsx b/src/app/pages/Explore/Tokens.test.tsx
index 02ec3f9a0..edb920222 100644
--- a/src/app/pages/Explore/Tokens.test.tsx
+++ b/src/app/pages/Explore/Tokens.test.tsx
@@ -1,6 +1,6 @@
import React from 'react';
-import { render, screen } from '@testing-library/react';
+import { fireEvent, render, screen } from '@testing-library/react';
import Tokens from './Tokens';
@@ -35,11 +35,19 @@ jest.mock('components/TokenLogo', () => ({
TokenLogo: () =>
}));
+const mockGetTokenPrice = jest.fn();
+jest.mock('lib/prices', () => ({
+ getTokenPrice: (...args: unknown[]) => mockGetTokenPrice(...args)
+}));
+
const mockUseAllBalances = jest.requireMock('lib/miden/front').useAllBalances;
describe('Tokens', () => {
beforeEach(() => {
jest.clearAllMocks();
+ // Default to a positive 24h change (green branch). Individual tests
+ // override this to exercise the negative (red) branch.
+ mockGetTokenPrice.mockReturnValue({ price: 1, percentageChange24h: 1 });
});
it('renders even when no balances are loaded yet', () => {
@@ -133,4 +141,64 @@ describe('Tokens', () => {
expect(container.querySelector('.animate-pulse')).not.toBeInTheDocument();
});
+
+ it('falls back to the symbol when a token has no name', () => {
+ mockUseAllBalances.mockReturnValue({
+ data: [
+ {
+ tokenId: 'token-noname',
+ balance: 1,
+ // No `name` — title should fall back to the symbol.
+ metadata: { symbol: 'NONAME', decimals: 8 }
+ }
+ ],
+ isLoading: false
+ });
+
+ render();
+
+ expect(screen.getByText('NONAME')).toBeInTheDocument();
+ });
+
+ it('renders a negative 24h price change (red branch)', () => {
+ mockGetTokenPrice.mockReturnValue({ price: 1, percentageChange24h: -3.5 });
+ mockUseAllBalances.mockReturnValue({
+ data: [{ tokenId: 'token-1', balance: 100, metadata: { symbol: 'TKN', name: 'Token', decimals: 8 } }],
+ isLoading: false
+ });
+
+ render();
+
+ expect(screen.getByText('Token')).toBeInTheDocument();
+ });
+
+ it('filters the token list by the search query (name and symbol)', () => {
+ mockUseAllBalances.mockReturnValue({
+ data: [
+ // Including the MIDEN faucet in a multi-token list ensures the sort
+ // comparator actually runs and hits its `=== midenFaucetId` branch.
+ { tokenId: 'miden-faucet-id', balance: 0, metadata: { symbol: 'MIDEN', name: 'Miden', decimals: 8 } },
+ { tokenId: 'a', balance: 5, metadata: { symbol: 'AAA', name: 'Alpha', decimals: 8 } },
+ { tokenId: 'b', balance: 5, metadata: { symbol: 'BBB', decimals: 8 } }
+ ],
+ isLoading: false
+ });
+
+ render();
+
+ // Both render before filtering.
+ expect(screen.getByText('Alpha')).toBeInTheDocument();
+ expect(screen.getByText('BBB')).toBeInTheDocument();
+
+ // Search by name — only the matching token survives.
+ fireEvent.change(screen.getByPlaceholderText('searchForToken'), { target: { value: 'alp' } });
+ expect(screen.getByText('Alpha')).toBeInTheDocument();
+ expect(screen.queryByText('BBB')).not.toBeInTheDocument();
+
+ // Search by symbol on the token that has no name (exercises the
+ // optional-chained name match short-circuit and the symbol branch).
+ fireEvent.change(screen.getByPlaceholderText('searchForToken'), { target: { value: 'bbb' } });
+ expect(screen.getByText('BBB')).toBeInTheDocument();
+ expect(screen.queryByText('Alpha')).not.toBeInTheDocument();
+ });
});
diff --git a/src/app/pages/Settings.selectors.ts b/src/app/pages/Settings.selectors.ts
index 59c952ccd..cfbb0b1c6 100644
--- a/src/app/pages/Settings.selectors.ts
+++ b/src/app/pages/Settings.selectors.ts
@@ -5,6 +5,7 @@ export enum SettingsSelectors {
AddressBookButton = 'Settings/AddressBookButton',
RevealViewKeyButton = 'Settings/RevealViewKeyButton',
RevealPrivateKeyButton = 'Settings/RevealPrivateKeyButton',
+ RevealHotKeyButton = 'Settings/RevealHotKeyButton',
RevealSeedPhraseButton = 'Settings/RevealSeedPhraseButton',
DAppsButton = 'Settings/DAppsButton',
NetworksButton = 'Settings/NetworksButton',
diff --git a/src/app/pages/Settings.tsx b/src/app/pages/Settings.tsx
index d037756ec..d8e43d404 100644
--- a/src/app/pages/Settings.tsx
+++ b/src/app/pages/Settings.tsx
@@ -70,7 +70,13 @@ type SettingsProps = {
tabSlug?: string | null;
};
-const RevealPrivateKey: FC = () => ;
+const RevealPrivateKey: FC = () => {
+ const currentAccountType = useWalletStore(s => s.currentAccount?.type);
+ const isGuardian = currentAccountType === WalletType.Guardian;
+ return ;
+};
+
+const RevealHotKey: FC = () => ;
const LANGUAGE_LABELS: Record = {
en: 'English',
@@ -103,6 +109,10 @@ type Tab = {
isDrawer?: boolean;
onClick?: () => void;
guardianOnly?: boolean;
+ // Hide on Guardian accounts whose hot key is not yet activated (post-recovery,
+ // pre-banner-click). The corresponding Settings flow needs a `hotPublicKey`
+ // set on the WalletAccount or it'll fail immediately on the vault lookup.
+ requiresActivatedHotKey?: boolean;
};
type TabGroup = {
@@ -158,6 +168,15 @@ const TAB_GROUPS: TabGroup[] = [
Component: RevealPrivateKey,
testID: SettingsSelectors.RevealPrivateKeyButton
},
+ {
+ slug: 'reveal-hot-key',
+ titleI18nKey: 'revealHotKey',
+ Icon: SecretKeyIcon,
+ Component: RevealHotKey,
+ testID: SettingsSelectors.RevealHotKeyButton,
+ guardianOnly: true,
+ requiresActivatedHotKey: true
+ },
{
slug: 'encrypted-wallet-file',
titleI18nKey: 'encryptedWalletFile',
@@ -245,7 +264,18 @@ const HIDDEN_TABS: Tab[] = [
const Settings: FC = ({ tabSlug }) => {
const { t } = useTranslation();
const currentAccountType = useWalletStore(s => s.currentAccount?.type);
+ const currentAccountHotPublicKey = useWalletStore(s => s.currentAccount?.hotPublicKey);
const isGuardianAccount = currentAccountType === WalletType.Guardian;
+ const hasActivatedHotKey = Boolean(currentAccountHotPublicKey);
+
+ const tabIsVisible = useCallback(
+ (tab: Tab) => {
+ if (tab.guardianOnly && !isGuardianAccount) return false;
+ if (tab.requiresActivatedHotKey && !hasActivatedHotKey) return false;
+ return true;
+ },
+ [hasActivatedHotKey, isGuardianAccount]
+ );
// Filter tabs that are gated to Guardian accounts. Non-Guardian users don't see
// the Guardian Settings entry at all (menu, drawer, or routable page).
@@ -253,14 +283,14 @@ const Settings: FC = ({ tabSlug }) => {
() =>
TAB_GROUPS.map(group => ({
...group,
- tabs: group.tabs.filter(tab => !tab.guardianOnly || isGuardianAccount)
+ tabs: group.tabs.filter(tabIsVisible)
})).filter(group => group.tabs.length > 0),
- [isGuardianAccount]
+ [tabIsVisible]
);
const allTabs = useMemo(
- () => [...tabGroups.flatMap(g => g.tabs), ...HIDDEN_TABS.filter(tab => !tab.guardianOnly || isGuardianAccount)],
- [tabGroups, isGuardianAccount]
+ () => [...tabGroups.flatMap(g => g.tabs), ...HIDDEN_TABS.filter(tabIsVisible)],
+ [tabGroups, tabIsVisible]
);
const drawerTabs = useMemo(() => tabGroups.flatMap(g => g.tabs).filter(t => t.isDrawer), [tabGroups]);
diff --git a/src/app/templates/ActivateHotKeyBanner.tsx b/src/app/templates/ActivateHotKeyBanner.tsx
new file mode 100644
index 000000000..8d79f721c
--- /dev/null
+++ b/src/app/templates/ActivateHotKeyBanner.tsx
@@ -0,0 +1,73 @@
+import React, { FC, useCallback, useState } from 'react';
+
+import classNames from 'clsx';
+import { useTranslation } from 'react-i18next';
+
+import { Icon, IconName } from 'app/icons/v2';
+import { initiateReplaceHotKeyTransaction, requestSWTransactionProcessing } from 'lib/miden/activity';
+import { useAccount } from 'lib/miden/front';
+import { zustandProvider } from 'lib/miden/front/guardian-sync';
+import { hapticLight } from 'lib/mobile/haptics';
+import { isExtension } from 'lib/platform';
+import { isDelegateProofEnabled } from 'lib/settings/helpers';
+import { useWalletStore } from 'lib/store';
+
+interface Props {
+ className?: string;
+}
+
+/**
+ * Surfaces post-recovery: Guardian accounts adopted via seed-phrase lookup have
+ * no usable local hot key (the on-chain hot's secret is unrecoverable). The
+ * banner CTA fires a cold-signed `replace_signer` rotation that mints a fresh
+ * hot key and swaps it on-chain. `requiresHotKeyRotation` clears once
+ * `Vault.swapHotKey` lands, at which point the banner self-hides.
+ */
+export const ActivateHotKeyBanner: FC = ({ className }) => {
+ const { t } = useTranslation();
+ const account = useAccount();
+ const [submitting, setSubmitting] = useState(false);
+ const [error, setError] = useState(null);
+
+ const onClick = useCallback(async () => {
+ if (submitting) return;
+ hapticLight();
+ setSubmitting(true);
+ setError(null);
+ try {
+ await initiateReplaceHotKeyTransaction(account.publicKey, isDelegateProofEnabled(), zustandProvider);
+ useWalletStore.getState().openTransactionModal();
+ if (isExtension()) requestSWTransactionProcessing();
+ } catch (e) {
+ setError(e instanceof Error ? e.message : String(e));
+ setSubmitting(false);
+ }
+ }, [account.publicKey, submitting]);
+
+ if (!account.requiresHotKeyRotation) return null;
+
+ return (
+
+
+
+
+
+
{t('activateHotKeyBannerTitle')}
+
{error ?? t('activateHotKeyBannerBody')}
+
+
+
+ );
+};
+
+export default ActivateHotKeyBanner;
diff --git a/src/app/templates/GuardianReplaceHotKey.tsx b/src/app/templates/GuardianReplaceHotKey.tsx
new file mode 100644
index 000000000..c372e458e
--- /dev/null
+++ b/src/app/templates/GuardianReplaceHotKey.tsx
@@ -0,0 +1,102 @@
+import React, { FC, useCallback, useState } from 'react';
+
+import { useTranslation } from 'react-i18next';
+
+import FormSubmitButton from 'app/atoms/FormSubmitButton';
+import {
+ initiateReplaceHotKeyTransaction,
+ requestSWTransactionProcessing,
+ waitForTransactionCompletion
+} from 'lib/miden/activity';
+import { zustandProvider } from 'lib/miden/front/guardian-sync';
+import { isExtension } from 'lib/platform';
+import { isDelegateProofEnabled } from 'lib/settings/helpers';
+import { useWalletStore } from 'lib/store';
+
+type Props = {
+ onClose?: () => void;
+};
+
+/**
+ * Proactive hot-key rotation. Cold-signed (recovery key); the on-chain proposal
+ * swaps the hot signer commitment in-place via update_signers. The seed phrase
+ * is NOT required — the cold key derived at create time is already in the vault.
+ */
+const GuardianReplaceHotKey: FC = ({ onClose }) => {
+ const { t } = useTranslation();
+ const currentAccount = useWalletStore(s => s.currentAccount);
+
+ const [confirming, setConfirming] = useState(false);
+ const [submitting, setSubmitting] = useState(false);
+ const [error, setError] = useState(null);
+ const [success, setSuccess] = useState(false);
+
+ const onClick = useCallback(async () => {
+ if (!currentAccount) return;
+ if (!confirming) {
+ setConfirming(true);
+ return;
+ }
+ setSubmitting(true);
+ setError(null);
+ setSuccess(false);
+ try {
+ const txId = await initiateReplaceHotKeyTransaction(
+ currentAccount.publicKey,
+ isDelegateProofEnabled(),
+ zustandProvider
+ );
+ useWalletStore.getState().openTransactionModal();
+ if (isExtension()) requestSWTransactionProcessing();
+
+ const result = await waitForTransactionCompletion(txId);
+ if ('errorMessage' in result) {
+ setError(result.errorMessage);
+ return;
+ }
+ setSuccess(true);
+ setConfirming(false);
+ } catch (e) {
+ setError(e instanceof Error ? e.message : String(e));
+ } finally {
+ setSubmitting(false);
+ }
+ }, [confirming, currentAccount]);
+
+ return (
+